From c4b04ed3dc5d111350d76079d51f68edba1e0bf2 Mon Sep 17 00:00:00 2001 From: jkhsjdhjs Date: Wed, 17 Jun 2020 15:04:32 +0200 Subject: [PATCH] add eslint + prettier reformat code with prettier --- .eslintrc.json | 15 + .prettierrc.json | 6 + .travis.yml | 24 +- README.md | 130 +++-- package-lock.json | 1176 +++++++++++++++++++++++++++++++++++++++++++- package.json | 70 +-- src/cookie-jar.mjs | 96 ++-- src/cookie.mjs | 162 +++--- src/errors.mjs | 15 +- src/index.mjs | 66 +-- test/cookie.mjs | 488 +++++++++++------- test/errors.mjs | 30 +- test/index.mjs | 56 ++- 13 files changed, 1889 insertions(+), 445 deletions(-) create mode 100644 .eslintrc.json create mode 100644 .prettierrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..69bded8 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "env": { + "es2020": true, + "node": true + }, + "extends": ["eslint:recommended", "plugin:prettier/recommended"], + "plugins": ["prettier"], + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "prettier/prettier": "error" + } +} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..d43b587 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "tabWidth": 4, + "trailingComma": "none", + "bracketSpacing": false, + "arrowParens": "avoid" +} diff --git a/.travis.yml b/.travis.yml index c8247a2..d1e13a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,17 @@ language: node_js node_js: -- node -- lts/* -- 11.14.0 + - node + - lts/* + - 11.14.0 script: -- node --experimental-modules test/index.mjs + - npm test deploy: - provider: npm - email: npm@totally.rip - api_key: - secure: 49x2b6FaWwTCfRXXl2CaQyADZ5cvWkxS/QHRjWOJaOyPVO0M/6H23EWGxu3xttUfKHv+RADrrWKiOnJhnOVD08wO/kpC3jizKQa7D3z08hLmt8lNf6I+gt3VjUnaM1JvEAtGruJcx0aUvbqEVEJ5EehPgE+4/IGCcYC0gF+xEU1ButyQprQspw333jAEBj+0EbwV3K8Tr8CdlpZHN2wh8uzSHGg1m6jMbVZcamI3uo1INoRzhGt5Fd4t5xPOfUYyFPMopCnXX8cwed5O3lgx8nFzhS7kSl8lJrCkhzrqmx/84s1mssEj6hvz6bH5zRwj9Om3wr/1N/Q76sP4Tf6kwbpMXZR/+1dUCgbdSV7XGqaUSDzsuOPUW9+cC/RBF0O0Cr/EfmvJx4Uy5pW/OcCAYBmmMxaupEkW583oGm3lTe66UW9x866j5HyG/z2ElsEDBJscpB2niB4pa71g+ypiJcWydruozOiejS+/Edy/cfM/JPmJv1BTpr8IntWKKsriVcrLXo1p+RMRAS1ZJzg+bU0wuBWTK4EbLE8663VBg4oTd9E1pwvpt5pnFKopeYtVLDX0Vtvd4+XQaUMf80Dx6mLrS1hWyLrt+fe1LX+1gNhwZFLDM0FZnnAaszzxVBAJlpD3F4hz7Jb6Q2ZRvNWP8DCNcJPtlcimSb8usk1eTDE= - on: - branch: master - tags: true - repo: jkhsjdhjs/node-fetch-cookies + provider: npm + email: npm@totally.rip + api_key: + secure: 49x2b6FaWwTCfRXXl2CaQyADZ5cvWkxS/QHRjWOJaOyPVO0M/6H23EWGxu3xttUfKHv+RADrrWKiOnJhnOVD08wO/kpC3jizKQa7D3z08hLmt8lNf6I+gt3VjUnaM1JvEAtGruJcx0aUvbqEVEJ5EehPgE+4/IGCcYC0gF+xEU1ButyQprQspw333jAEBj+0EbwV3K8Tr8CdlpZHN2wh8uzSHGg1m6jMbVZcamI3uo1INoRzhGt5Fd4t5xPOfUYyFPMopCnXX8cwed5O3lgx8nFzhS7kSl8lJrCkhzrqmx/84s1mssEj6hvz6bH5zRwj9Om3wr/1N/Q76sP4Tf6kwbpMXZR/+1dUCgbdSV7XGqaUSDzsuOPUW9+cC/RBF0O0Cr/EfmvJx4Uy5pW/OcCAYBmmMxaupEkW583oGm3lTe66UW9x866j5HyG/z2ElsEDBJscpB2niB4pa71g+ypiJcWydruozOiejS+/Edy/cfM/JPmJv1BTpr8IntWKKsriVcrLXo1p+RMRAS1ZJzg+bU0wuBWTK4EbLE8663VBg4oTd9E1pwvpt5pnFKopeYtVLDX0Vtvd4+XQaUMf80Dx6mLrS1hWyLrt+fe1LX+1gNhwZFLDM0FZnnAaszzxVBAJlpD3F4hz7Jb6Q2ZRvNWP8DCNcJPtlcimSb8usk1eTDE= + on: + branch: master + tags: true + repo: jkhsjdhjs/node-fetch-cookies diff --git a/README.md b/README.md index 266a7d8..c87c96c 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ It supports reading/writing from/to a JSON cookie jar and keeps cookies in memor ### For upgrading from 1.2.x or below to 1.3.x or above, please read the [breaking API changes](#130-breaking-api-changes). - ## Usage Examples + ### with file... + ```javascript import {fetch, CookieJar} from "node-fetch-cookies"; @@ -27,6 +28,7 @@ import {fetch, CookieJar} from "node-fetch-cookies"; ``` ### ...or without + ```javascript import {fetch, CookieJar} from "node-fetch-cookies"; @@ -40,125 +42,155 @@ import {fetch, CookieJar} from "node-fetch-cookies"; }); // do some requests you require login for - response = await fetch(cookieJar, "https://example.com/api/admin/drop-all-databases"); + response = await fetch( + cookieJar, + "https://example.com/api/admin/drop-all-databases" + ); // and optionally log out again response = await fetch(cookieJar, "https://example.com/api/logout"); })(); ``` - ## Documentation ### async fetch(cookieJars, url[, options]) -- `cookieJars` A [CookieJar](#class-cookiejar) instance, an array of CookieJar instances or null, if you don't want to send or store cookies. -- `url` and `options` as in https://github.com/bitinn/node-fetch#fetchurl-options + +- `cookieJars` A [CookieJar](#class-cookiejar) instance, an array of CookieJar instances or null, if you don't want to send or store cookies. +- `url` and `options` as in https://github.com/bitinn/node-fetch#fetchurl-options Returns a Promise resolving to a [Response](https://github.com/bitinn/node-fetch#class-response) instance on success. ### Class: CookieJar + A class that stores cookies. #### Properties -- `flags` The read/write flags as specified below. -- `file` The path of the cookie jar on the disk. -- `cookies` A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) mapping hostnames to maps, which map cookie names to the respective [Cookie](#class-cookie) instance. + +- `flags` The read/write flags as specified below. +- `file` The path of the cookie jar on the disk. +- `cookies` A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) mapping hostnames to maps, which map cookie names to the respective [Cookie](#class-cookie) instance. #### new CookieJar([file, flags = `rw`, cookies, cookieIgnoreCallback]) -- `file` An optional string containing a relative or absolute path to the file on the disk to use. -- `flags` An optional string specifying whether cookies should be read and/or written from/to the jar when passing it as parameter to [fetch](#fetchcookiejar-url-options). Default: `rw` - - `r`: only read from this jar - - `w`: only write to this jar - - `rw` or `wr`: read/write from/to this jar -- `cookies` An optional initializer for the cookie jar - either an array of [Cookie](#class-cookie) instances or a single Cookie instance. -- `cookieIgnoreCallback(cookie, reason)` An optional callback function which will be called when a cookie is ignored instead of added to the cookie jar. - - `cookie` The cookie string - - `reason` A string containing the reason why the cookie has been ignored + +- `file` An optional string containing a relative or absolute path to the file on the disk to use. +- `flags` An optional string specifying whether cookies should be read and/or written from/to the jar when passing it as parameter to [fetch](#fetchcookiejar-url-options). Default: `rw` + - `r`: only read from this jar + - `w`: only write to this jar + - `rw` or `wr`: read/write from/to this jar +- `cookies` An optional initializer for the cookie jar - either an array of [Cookie](#class-cookie) instances or a single Cookie instance. +- `cookieIgnoreCallback(cookie, reason)` An optional callback function which will be called when a cookie is ignored instead of added to the cookie jar. + - `cookie` The cookie string + - `reason` A string containing the reason why the cookie has been ignored #### addCookie(cookie[, fromURL]) + Adds a cookie to the jar. -- `cookie` A [Cookie](#class-cookie) instance to add to the cookie jar. -Alternatively this can also be a string, for example a serialized cookie received from a website. -In this case `fromURL` must be specified. -- `fromURL` The url a cookie has been received from. + +- `cookie` A [Cookie](#class-cookie) instance to add to the cookie jar. + Alternatively this can also be a string, for example a serialized cookie received from a website. + In this case `fromURL` must be specified. +- `fromURL` The url a cookie has been received from. Returns `true` if the cookie has been added successfully. Returns `false` otherwise. If the parser throws a [CookieParseError](#class-cookieparseerror) it will be caught and a warning will be printed to console. #### domains() + Returns an iterator over all domains currently stored cookies for. -#### *cookiesDomain(domain) +#### \*cookiesDomain(domain) + Returns an iterator over all cookies currently stored for `domain`. -#### *cookiesValid(withSession) -Returns an iterator over all valid (non-expired) cookies. -- `withSession`: A boolean. Iterator will include session cookies if set to `true`. +#### \*cookiesValid(withSession) + +Returns an iterator over all valid (non-expired) cookies. + +- `withSession`: A boolean. Iterator will include session cookies if set to `true`. + +#### \*cookiesAll() -#### *cookiesAll() Returns an iterator over all cookies currently stored. -#### *cookiesValidForRequest(requestURL) +#### \*cookiesValidForRequest(requestURL) + Returns an iterator over all cookies valid for a request to `url`. #### deleteExpired(sessionEnded) + Removes all expired cookies from the jar. -- `sessionEnded`: A boolean. Also removes session cookies if set to `true`. + +- `sessionEnded`: A boolean. Also removes session cookies if set to `true`. #### async load([file = this.file]) + Reads cookies from `file` on the disk and adds the contained cookies. -- `file`: Path to the file where the cookies should be saved. Default: `this.file`, the file that has been passed to the constructor. + +- `file`: Path to the file where the cookies should be saved. Default: `this.file`, the file that has been passed to the constructor. #### async save([file = this.file]) + Saves the cookie jar to `file` on the disk. Only non-expired non-session cookies are saved. -- `file`: Path to the file where the cookies should be saved. Default: `this.file`, the file that has been passed to the constructor. + +- `file`: Path to the file where the cookies should be saved. Default: `this.file`, the file that has been passed to the constructor. ### Class: Cookie + An abstract representation of a cookie. #### Properties -- `name` The identifier of the cookie. -- `value` The value of the cookie. -- `expiry` A [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object of the cookies expiry date or `null`, if the cookie expires with the session. -- `domain` The domain the cookie is valid for. -- `path` The path the cookie is valid for. -- `secure` A boolean value representing the cookie's secure attribute. If set the cookie will only be used for `https` requests. -- `subdomains` A boolean value specifying whether the cookie should be used for requests to subdomains of `domain` or not. + +- `name` The identifier of the cookie. +- `value` The value of the cookie. +- `expiry` A [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object of the cookies expiry date or `null`, if the cookie expires with the session. +- `domain` The domain the cookie is valid for. +- `path` The path the cookie is valid for. +- `secure` A boolean value representing the cookie's secure attribute. If set the cookie will only be used for `https` requests. +- `subdomains` A boolean value specifying whether the cookie should be used for requests to subdomains of `domain` or not. #### new Cookie(str, requestURL) + Creates a cookie instance from the string representation of a cookie as send by a webserver. -- `str` The string representation of a cookie. -- `url` The url the cookie has been received from. + +- `str` The string representation of a cookie. +- `url` The url the cookie has been received from. Will throw a `CookieParseError` if `str` couldn't be parsed. #### static fromObject(obj) + Creates a cookie instance from an already existing object with the same properties. #### serialize() + Serializes the cookie, transforming it to `name=value` so it can be used in requests. #### hasExpired(sessionEnded) + Returns whether the cookie has expired or not. -- `sessionEnded`: A boolean that specifies whether the current session has ended, meaning if set to `true`, the function will return `true` for session cookies. + +- `sessionEnded`: A boolean that specifies whether the current session has ended, meaning if set to `true`, the function will return `true` for session cookies. #### isValidForRequest(requestURL) + Returns whether the cookie is valid for a request to `url`. ### Class: CookieParseError + The Error that is thrown when the cookie parser located in the constructor of the [Cookie](#class-cookie) class is unable to parse the input. - ## 1.3.0 Breaking API Changes -- `new CookieJar(flags, file, cookies)` has been changed to `new CookieJar(file, flags = "rw", cookies)`. -`new CookieJar("rw")` can now be written as `new CookieJar()`, `new CookieJar("rw", "jar.json")` can now be written as `new CookieJar("jar.json")`. -This change has been introduced to simplify the usage of this library, since `rw` is used for `flags` in most cases anyways. -- `CookieJar.addFromFile(file)` has been renamed to the async function `async CookieJar.load([file = this.file])`, which uses the fsPromises API for non-blocking cookie loading. -The default value for `file` is the file passed to the constructor. -- `CookieJar.save(file)` was moved to `async CookieJar.save([file = this.file])` now also uses the fsPromises API. -- `new CookieJar()` now doesn't load cookies from the specified file anymore. To do so, call `await CookieJar.load()` after creating the CookieJar. -**NOTE: `CookieJar.load()` will throw an error if the cookie jar doesn't exist or doesn't contain valid JSON!** +- `new CookieJar(flags, file, cookies)` has been changed to `new CookieJar(file, flags = "rw", cookies)`. + `new CookieJar("rw")` can now be written as `new CookieJar()`, `new CookieJar("rw", "jar.json")` can now be written as `new CookieJar("jar.json")`. + This change has been introduced to simplify the usage of this library, since `rw` is used for `flags` in most cases anyways. +- `CookieJar.addFromFile(file)` has been renamed to the async function `async CookieJar.load([file = this.file])`, which uses the fsPromises API for non-blocking cookie loading. + The default value for `file` is the file passed to the constructor. +- `CookieJar.save(file)` was moved to `async CookieJar.save([file = this.file])` now also uses the fsPromises API. +- `new CookieJar()` now doesn't load cookies from the specified file anymore. To do so, call `await CookieJar.load()` after creating the CookieJar. + **NOTE: `CookieJar.load()` will throw an error if the cookie jar doesn't exist or doesn't contain valid JSON!** ## License + This project is licensed under the MIT license, see [LICENSE](LICENSE). diff --git a/package-lock.json b/package-lock.json index e85f537..6b3ebef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,1169 @@ { - "name": "node-fetch-cookies", - "version": "1.3.6", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "name": "node-fetch-cookies", + "version": "1.3.6", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", + "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.1" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", + "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.2.0.tgz", + "integrity": "sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.0.0", + "eslint-visitor-keys": "^1.2.0", + "espree": "^7.1.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-config-prettier": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", + "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-plugin-prettier": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", + "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", + "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", + "dev": true + }, + "espree": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", + "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", + "dev": true, + "requires": { + "acorn": "^7.2.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.2.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.2.0.tgz", + "integrity": "sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + } } - } } diff --git a/package.json b/package.json index 658e745..3dd3ffa 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,40 @@ { - "name": "node-fetch-cookies", - "version": "1.3.6", - "description": "node-fetch wrapper that adds support for cookie-jars", - "main": "src/index.mjs", - "engines": { - "node": ">=11.14.0" - }, - "files": [ - "src/" - ], - "scripts": { - "test": "node test/index.mjs" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/jkhsjdhjs/node-fetch-cookies.git" - }, - "keywords": [ - "cookie", - "cookie-jar", - "node-fetch", - "fetch" - ], - "author": "jkhsjdhjs", - "license": "MIT", - "bugs": { - "url": "https://github.com/jkhsjdhjs/node-fetch-cookies/issues" - }, - "homepage": "https://github.com/jkhsjdhjs/node-fetch-cookies#readme", - "dependencies": { - "node-fetch": "^2.6.0" - } + "name": "node-fetch-cookies", + "version": "1.3.6", + "description": "node-fetch wrapper that adds support for cookie-jars", + "main": "src/index.mjs", + "engines": { + "node": ">=11.14.0" + }, + "files": [ + "src/" + ], + "scripts": { + "test": "npx eslint --ext mjs . && npx prettier --check package.json package-lock.json .travis.yml .prettierrc.json .eslintrc.json README.md && node --experimental-modules test/index.mjs" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/jkhsjdhjs/node-fetch-cookies.git" + }, + "keywords": [ + "cookie", + "cookie-jar", + "node-fetch", + "fetch" + ], + "author": "jkhsjdhjs", + "license": "MIT", + "bugs": { + "url": "https://github.com/jkhsjdhjs/node-fetch-cookies/issues" + }, + "homepage": "https://github.com/jkhsjdhjs/node-fetch-cookies#readme", + "dependencies": { + "node-fetch": "^2.6.0" + }, + "devDependencies": { + "eslint": "^7.2.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-prettier": "^3.1.4", + "prettier": "2.0.5" + } } diff --git a/src/cookie-jar.mjs b/src/cookie-jar.mjs index e05e158..6f3978a 100644 --- a/src/cookie-jar.mjs +++ b/src/cookie-jar.mjs @@ -6,43 +6,55 @@ import {paramError, CookieParseError} from "./errors.mjs"; export default class CookieJar { constructor(file, flags = "rw", cookies, cookieIgnoreCallback) { this.cookies = new Map(); - - if(file && typeof file !== "string") + + if (file && typeof file !== "string") throw paramError("Second", "file", "new CookieJar()", "string"); - if(typeof flags !== "string") + if (typeof flags !== "string") throw paramError("First", "flags", "new CookieJar()", "string"); - if(Array.isArray(cookies)) { - if(!cookies.every(c => c instanceof Cookie)) - throw paramError("Third", "cookies", "new CookieJar()", "[Cookie]"); + if (Array.isArray(cookies)) { + if (!cookies.every(c => c instanceof Cookie)) + throw paramError( + "Third", + "cookies", + "new CookieJar()", + "[Cookie]" + ); cookies.forEach(cookie => this.addCookie(cookie)); - } - else if(cookies instanceof Cookie) - this.addCookie(cookies); - else if(cookies) - throw paramError("Third", "cookies", "new CookieJar()", ["[Cookie]", "Cookie"]); - if(cookieIgnoreCallback && typeof cookieIgnoreCallback !== "function") - throw paramError("Fourth", "cookieIgnoreCallback", "new CookieJar()", "function"); + } else if (cookies instanceof Cookie) this.addCookie(cookies); + else if (cookies) + throw paramError("Third", "cookies", "new CookieJar()", [ + "[Cookie]", + "Cookie" + ]); + if (cookieIgnoreCallback && typeof cookieIgnoreCallback !== "function") + throw paramError( + "Fourth", + "cookieIgnoreCallback", + "new CookieJar()", + "function" + ); this.file = file; this.flags = flags; this.cookieIgnoreCallback = cookieIgnoreCallback; } addCookie(cookie, fromURL) { - if(typeof cookie === "string") { + if (typeof cookie === "string") { try { cookie = new Cookie(cookie, fromURL); - } - catch(error) { - if(error instanceof CookieParseError) { - if(this.cookieIgnoreCallback) + } catch (error) { + if (error instanceof CookieParseError) { + if (this.cookieIgnoreCallback) this.cookieIgnoreCallback(cookie, error.message); return false; } throw error; } - } - else if(!(cookie instanceof Cookie)) - throw paramError("First", "cookie", "CookieJar.addCookie()", ["string", "Cookie"]); - if(!this.cookies.get(cookie.domain)) + } else if (!(cookie instanceof Cookie)) + throw paramError("First", "cookie", "CookieJar.addCookie()", [ + "string", + "Cookie" + ]); + if (!this.cookies.get(cookie.domain)) this.cookies.set(cookie.domain, new Map()); this.cookies.get(cookie.domain).set(cookie.name, cookie); return true; @@ -51,30 +63,29 @@ export default class CookieJar { return this.cookies.keys(); } *cookiesDomain(domain) { - for(const cookie of (this.cookies.get(domain) || []).values()) + for (const cookie of (this.cookies.get(domain) || []).values()) yield cookie; } *cookiesValid(withSession) { - for(const cookie of this.cookiesAll()) - if(!cookie.hasExpired(!withSession)) - yield cookie; + for (const cookie of this.cookiesAll()) + if (!cookie.hasExpired(!withSession)) yield cookie; } *cookiesAll() { - for(const domain of this.domains()) - yield* this.cookiesDomain(domain); + for (const domain of this.domains()) yield* this.cookiesDomain(domain); } *cookiesValidForRequest(requestURL) { const namesYielded = new Set(), - domains = url + domains = url .parse(requestURL) - .hostname - .split(".") + .hostname.split(".") .map((_, i, a) => a.slice(i).join(".")) .slice(0, -1); - for(const domain of domains) { - for(const cookie of this.cookiesDomain(domain)) { - if(cookie.isValidForRequest(requestURL) - && !namesYielded.has(cookie.name)) { + for (const domain of domains) { + for (const cookie of this.cookiesDomain(domain)) { + if ( + cookie.isValidForRequest(requestURL) && + !namesYielded.has(cookie.name) + ) { namesYielded.add(cookie.name); yield cookie; } @@ -87,14 +98,19 @@ export default class CookieJar { validCookies.forEach(c => this.addCookie(c)); } async load(file = this.file) { - if(typeof file !== "string") + if (typeof file !== "string") throw new Error("No file has been specified for this cookie jar!"); - JSON.parse(await fs.readFile(file)).forEach(c => this.addCookie(Cookie.fromObject(c))); + JSON.parse(await fs.readFile(file)).forEach(c => + this.addCookie(Cookie.fromObject(c)) + ); } async save(file = this.file) { - if(typeof file !== "string") + if (typeof file !== "string") throw new Error("No file has been specified for this cookie jar!"); // only save cookies that haven't expired - await fs.writeFile(this.file, JSON.stringify([...this.cookiesValid(false)])); + await fs.writeFile( + this.file, + JSON.stringify([...this.cookiesValid(false)]) + ); } -}; +} diff --git a/src/cookie.mjs b/src/cookie.mjs index 6c85ed2..a0dc288 100644 --- a/src/cookie.mjs +++ b/src/cookie.mjs @@ -4,7 +4,10 @@ import {paramError, CookieParseError} from "./errors.mjs"; const validateHostname = (cookieHostname, requestHostname, subdomains) => { cookieHostname = cookieHostname.toLowerCase(); requestHostname = requestHostname.toLowerCase(); - if(requestHostname === cookieHostname || (subdomains && requestHostname.endsWith("." + cookieHostname))) + if ( + requestHostname === cookieHostname || + (subdomains && requestHostname.endsWith("." + cookieHostname)) + ) return true; return false; }; @@ -12,16 +15,14 @@ const validateHostname = (cookieHostname, requestHostname, subdomains) => { const validatePath = (cookiePath, requestPath) => { cookiePath = decodeURIComponent(cookiePath).toLowerCase(); requestPath = decodeURIComponent(requestPath).toLowerCase(); - if(cookiePath.endsWith("/")) - cookiePath = cookiePath.slice(0, -1); - if(requestPath.endsWith("/")) - requestPath = requestPath.slice(0, -1); + if (cookiePath.endsWith("/")) cookiePath = cookiePath.slice(0, -1); + if (requestPath.endsWith("/")) requestPath = requestPath.slice(0, -1); return (requestPath + "/").startsWith(cookiePath + "/"); }; const splitN = (str, sep, n) => { const splitted = str.split(sep); - if(n < splitted.length - 1) { + if (n < splitted.length - 1) { splitted[n] = splitted.slice(n).join(sep); splitted.splice(n + 1); } @@ -30,9 +31,9 @@ const splitN = (str, sep, n) => { export default class Cookie { constructor(str, requestURL) { - if(typeof str !== "string") + if (typeof str !== "string") throw paramError("First", "str", "new Cookie()", "string"); - if(typeof requestURL !== "string") + if (typeof requestURL !== "string") throw paramError("Second", "requestURL", "new Cookie()", "string"); // check if url is valid @@ -40,95 +41,134 @@ export default class Cookie { const splitted = str.split("; "); [this.name, this.value] = splitN(splitted[0], "=", 1); - if(!this.name) - throw new CookieParseError("Invalid cookie name \"" + this.name + "\"!"); - if(this.value.startsWith("\"") && this.value.endsWith("\"")) + if (!this.name) + throw new CookieParseError( + 'Invalid cookie name "' + this.name + '"!' + ); + if (this.value.startsWith('"') && this.value.endsWith('"')) this.value = this.value.slice(1, -1); const parsedURL = url.parse(requestURL); - for(let i = 1; i < splitted.length; i++) { + for (let i = 1; i < splitted.length; i++) { let [k, v] = splitN(splitted[i], "=", 1); k = k.toLowerCase(); - if(v) { - if(k === "expires") { - if(this.expiry) // max-age has precedence over expires + if (v) { + if (k === "expires") { + if (this.expiry) + // max-age has precedence over expires continue; - if(!/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2}[ -](?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ -]\d{2,4} \d{2}:\d{2}:\d{2} GMT$/.test(v) - || (this.expiry = new Date(v)).toString() === "Invalid Date" - || this.expiry.getTime() < 0) - throw new CookieParseError("Invalid value for Expires \"" + v + "\"!"); - } - else if(k === "max-age") { + if ( + !/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2}[ -](?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ -]\d{2,4} \d{2}:\d{2}:\d{2} GMT$/.test( + v + ) || + (this.expiry = new Date(v)).toString() === + "Invalid Date" || + this.expiry.getTime() < 0 + ) + throw new CookieParseError( + 'Invalid value for Expires "' + v + '"!' + ); + } else if (k === "max-age") { const seconds = ~~+v; - if(seconds.toString() !== v) - throw new CookieParseError("Invalid value for Max-Age \"" + v + "\"!"); + if (seconds.toString() !== v) + throw new CookieParseError( + 'Invalid value for Max-Age "' + v + '"!' + ); this.expiry = new Date(); this.expiry.setSeconds(this.expiry.getSeconds() + seconds); - } - else if(k === "domain") { - if(v.startsWith(".")) - v = v.substring(1); - if(!validateHostname(parsedURL.hostname, v, true)) - throw new CookieParseError("Invalid value for Domain \"" + v + "\": cookie was received from \"" + parsedURL.hostname + "\"!"); + } else if (k === "domain") { + if (v.startsWith(".")) v = v.substring(1); + if (!validateHostname(parsedURL.hostname, v, true)) + throw new CookieParseError( + 'Invalid value for Domain "' + + v + + '": cookie was received from "' + + parsedURL.hostname + + '"!' + ); this.domain = v; this.subdomains = true; - } - else if(k === "path") - this.path = v; - else if(k === "samesite") // only relevant for cross site requests, so not for us + } else if (k === "path") this.path = v; + else if (k === "samesite") + // only relevant for cross site requests, so not for us continue; else - throw new CookieParseError("Invalid key \"" + k + "\" with value \"" + v + "\" specified!"); - } - else { - if(k === "secure") - this.secure = true; - else if(k === "httponly") // only relevant for browsers + throw new CookieParseError( + 'Invalid key "' + + k + + '" with value "' + + v + + '" specified!' + ); + } else { + if (k === "secure") this.secure = true; + else if (k === "httponly") + // only relevant for browsers continue; else - throw new CookieParseError("Invalid key \"" + k + "\" specified!"); + throw new CookieParseError( + 'Invalid key "' + k + '" specified!' + ); } } - if(this.name.toLowerCase().startsWith("__secure-") && (!this.secure || parsedURL.protocol !== "https:")) - throw new CookieParseError("Cookie has \"__Secure-\" prefix but \"Secure\" isn't set or the cookie is not set via https!"); - if(this.name.toLowerCase().startsWith("__host-") && (!this.secure || parsedURL.protocol !== "https:" || this.domain || this.path !== "/")) - throw new CookieParseError("Cookie has \"__Host-\" prefix but \"Secure\" isn't set, the cookie is not set via https, \"Domain\" is set or \"Path\" is not equal to \"/\"!"); + if ( + this.name.toLowerCase().startsWith("__secure-") && + (!this.secure || parsedURL.protocol !== "https:") + ) + throw new CookieParseError( + 'Cookie has "__Secure-" prefix but "Secure" isn\'t set or the cookie is not set via https!' + ); + if ( + this.name.toLowerCase().startsWith("__host-") && + (!this.secure || + parsedURL.protocol !== "https:" || + this.domain || + this.path !== "/") + ) + throw new CookieParseError( + 'Cookie has "__Host-" prefix but "Secure" isn\'t set, the cookie is not set via https, "Domain" is set or "Path" is not equal to "/"!' + ); // assign defaults - if(!this.domain) { + if (!this.domain) { this.domain = parsedURL.hostname; this.subdomains = false; } - if(!this.path) - this.path = "/"; - if(!this.secure) - this.secure = false; - if(!this.expiry) - this.expiry = null; + if (!this.path) this.path = "/"; + if (!this.secure) this.secure = false; + if (!this.expiry) this.expiry = null; } static fromObject(obj) { let c = Object.assign(Object.create(this.prototype), obj); - if(typeof c.expiry === "string") - c.expiry = new Date(c.expiry); + if (typeof c.expiry === "string") c.expiry = new Date(c.expiry); return c; } serialize() { return this.name + "=" + this.value; } hasExpired(sessionEnded) { - return sessionEnded && this.expiry === null || this.expiry && this.expiry < new Date(); + return ( + (sessionEnded && this.expiry === null) || + (this.expiry && this.expiry < new Date()) + ); } isValidForRequest(requestURL) { - if(this.hasExpired(false)) - return false; + if (this.hasExpired(false)) return false; const parsedURL = url.parse(requestURL); - if(parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:" - || this.secure && parsedURL.protocol !== "https:" - || !validateHostname(this.domain, parsedURL.hostname, this.subdomains) - || !validatePath(this.path, parsedURL.pathname)) + if ( + (parsedURL.protocol !== "http:" && + parsedURL.protocol !== "https:") || + (this.secure && parsedURL.protocol !== "https:") || + !validateHostname( + this.domain, + parsedURL.hostname, + this.subdomains + ) || + !validatePath(this.path, parsedURL.pathname) + ) return false; return true; } -}; +} diff --git a/src/errors.mjs b/src/errors.mjs index 94a3e6a..668dbcc 100644 --- a/src/errors.mjs +++ b/src/errors.mjs @@ -3,10 +3,15 @@ export class CookieParseError extends Error { super(...args); this.name = "CookieParseError"; } -}; +} export function paramError(position, paramName, functionName, validTypes) { - validTypes = [validTypes].flat().map(t => "\"" + t + "\""); - validTypes = validTypes.slice(0, -1).join(", ") + (validTypes.length > 1 ? " or " : "") + validTypes.slice(-1); - return new TypeError(`${position} parameter "${paramName}" passed to "${functionName}" is not of type ${validTypes}!`); -}; + validTypes = [validTypes].flat().map(t => '"' + t + '"'); + validTypes = + validTypes.slice(0, -1).join(", ") + + (validTypes.length > 1 ? " or " : "") + + validTypes.slice(-1); + return new TypeError( + `${position} parameter "${paramName}" passed to "${functionName}" is not of type ${validTypes}!` + ); +} diff --git a/src/index.mjs b/src/index.mjs index c55a460..039ca26 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -6,56 +6,64 @@ import {paramError, CookieParseError} from "./errors.mjs"; const redirectStatus = new Set([301, 302, 303, 307, 308]); async function fetch(cookieJars, url, options) { - let cookies = ""; const addValidFromJars = jars => { // since multiple cookie jars can be passed, filter duplicates by using a set of cookie names const set = new Set(); - jars.flatMap(jar => [...jar.cookiesValidForRequest(url)]) - .forEach(cookie => { - if(set.has(cookie.name)) - return; + jars.flatMap(jar => [...jar.cookiesValidForRequest(url)]).forEach( + cookie => { + if (set.has(cookie.name)) return; set.add(cookie.name); cookies += cookie.serialize() + "; "; - }); + } + ); }; - if(cookieJars) { - if(Array.isArray(cookieJars) && cookieJars.every(c => c instanceof CookieJar)) + if (cookieJars) { + if ( + Array.isArray(cookieJars) && + cookieJars.every(c => c instanceof CookieJar) + ) addValidFromJars(cookieJars.filter(jar => jar.flags.includes("r"))); - else if(cookieJars instanceof CookieJar) - if(cookieJars.flags.includes("r")) - addValidFromJars([cookieJars]); - else - throw paramError("First", "cookieJars", "fetch", ["CookieJar", "[CookieJar]"]); + else if (cookieJars instanceof CookieJar) + if (cookieJars.flags.includes("r")) addValidFromJars([cookieJars]); + else + throw paramError("First", "cookieJars", "fetch", [ + "CookieJar", + "[CookieJar]" + ]); } - if(cookies) { - if(!options) - options = {}; - if(!options.headers) - options.headers = {}; + if (cookies) { + if (!options) options = {}; + if (!options.headers) options.headers = {}; options.headers.cookie = cookies.slice(0, -2); } - const wantFollow = !options || !options.redirect || options.redirect === 'follow'; - if(wantFollow) { + const wantFollow = + !options || !options.redirect || options.redirect === "follow"; + if (wantFollow) { if (!options) options = {}; - options.redirect = 'manual'; + options.redirect = "manual"; } const result = await _fetch(url, options); // I cannot use headers.get() here because it joins the cookies to a string - cookies = result.headers[Object.getOwnPropertySymbols(result.headers)[0]]["set-cookie"]; - if(cookies && cookieJars) { - if(Array.isArray(cookieJars)) { + cookies = + result.headers[Object.getOwnPropertySymbols(result.headers)[0]][ + "set-cookie" + ]; + if (cookies && cookieJars) { + if (Array.isArray(cookieJars)) { cookieJars .filter(jar => jar.flags.includes("w")) .forEach(jar => cookies.forEach(c => jar.addCookie(c, url))); - } - else if(cookieJars instanceof CookieJar && cookieJars.flags.includes("w")) + } else if ( + cookieJars instanceof CookieJar && + cookieJars.flags.includes("w") + ) cookies.forEach(c => cookieJars.addCookie(c, url)); } - if(wantFollow && redirectStatus.has(result.status)) { - const location = result.headers.get('Location'); - options.redirect = 'follow'; + if (wantFollow && redirectStatus.has(result.status)) { + const location = result.headers.get("Location"); + options.redirect = "follow"; return fetch(cookieJars, location, options); } return result; diff --git a/test/cookie.mjs b/test/cookie.mjs index c939af6..62fd2b5 100644 --- a/test/cookie.mjs +++ b/test/cookie.mjs @@ -4,282 +4,412 @@ import {CookieParseError} from "../src/errors.mjs"; export default Test => [ new Test("new Cookie() / cookie parser", () => { const inputs = [ - [ // type error + [ + // type error 123, "" ], - [ // type error + [ + // type error "id=a3fWa", 123 ], - [ // type error invalid url + [ + // type error invalid url "id=a3fWa", "" ], - [ // type error invalid url + [ + // type error invalid url "id=a3fWa", "github.com" ], // TODO: fix this test case, parser shouldn't allow it. it is currently ignored - [ // type error invalid url - "id=a3fWa", + [ + // type error invalid url + "id=a3fWa", "https:abc/abs" ], - [ // cookie parse error invalid cookie name + [ + // cookie parse error invalid cookie name "", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28: GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Onv 2015 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Oct 20151 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 32 Oct 2015 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Oct 2015 25:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Oct 2015 07:61:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 UTC; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT+2; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=San, 21 Onv 2015 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Expires=Wed, 31 Dec 1969 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid value for expires + [ + // cookie parse error invalid value for expires "id=a3fWa; Max-Age=121252a; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid key secur + [ + // cookie parse error invalid key secur "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secur; HttpOnly", "https://github.com" ], - [ // cookie parse error invalid key HttpOly with value 2 + [ + // cookie parse error invalid key HttpOly with value 2 "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOly=2", "https://github.com" ], - [ // cookie parse error not set via https + [ + // cookie parse error not set via https "__Secure-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly", "http://github.com" ], - [ // cookie parse error secure not set + [ + // cookie parse error secure not set "__Secure-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; HttpOnly", "https://github.com" ], - [ // cookie parse error secure not set + [ + // cookie parse error secure not set "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; HttpOnly; Path=/", "https://github.com" ], - [ // cookie parse error not set via https + [ + // cookie parse error not set via https "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/", "http://github.com" ], - [ // cookie parse error domain is set + [ + // cookie parse error domain is set "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Domain=github.com; Path=/", "https://github.com" ], - [ // cookie parse error path is not set + [ + // cookie parse error path is not set "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // cookie parse error path is not equal to / + [ + // cookie parse error path is not equal to / "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/lel/", "https://github.com" ], - [ // cookie parse error domain is not a subdomain + [ + // cookie parse error domain is not a subdomain "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Domain=github.com", "https://gist.github.com" ], - [ // cookie parse error domain is not a subdomain + [ + // cookie parse error domain is not a subdomain "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Domain=npmjs.com", "https://gist.github.com" ], - [ // success + [ + // success "__Secure-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly", "https://github.com" ], - [ // success + [ + // success "__Host-id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/", "https://github.com" ], - [ // success + [ + // success "__Host-id=a3fWa; Expires=Wed, 21 Nov 2099 20:28:33 GMT; Secure; HttpOnly; Path=/", "https://github.com" ], - [ // success + [ + // success "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; Path=/lul/; Domain=.usercontent.github.com", "https://github.com" ], - [ // success + [ + // success "id=a3fWa; Expires=Wed, 21 Nov 2015 07:28:00 GMT; Secure; HttpOnly; SameSite=Strict; Path=/lul/; Domain=usercontent.github.com", "https://github.com" ], - [ // success max-age takes precendence over expires + [ + // success max-age takes precendence over expires "id=a3fWa; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=1000", "https://github.com" ], - [ // success max-age takes precendence over expires - "id=\"a3fWa\"; Max-Age=1000; Expires=Thu, 01 Jan 1970 00:00:00 GMT", + [ + // success max-age takes precendence over expires + 'id="a3fWa"; Max-Age=1000; Expires=Thu, 01 Jan 1970 00:00:00 GMT', "https://github.com" - ], + ] ]; const catchErrorTest = (input, catchFnc) => { try { new Cookie(...input); return false; - } - catch(error) { + } catch (error) { return catchFnc(error); } }; - const catchErrorTypeMessageTest = (input, type, message) => catchErrorTest(input, e => e instanceof type && e.message === message); + const catchErrorTypeMessageTest = (input, type, message) => + catchErrorTest( + input, + e => e instanceof type && e.message === message + ); const compareCookieProps = (input, expiryFnc, properties) => { const cookie = new Cookie(...input); - return Object.entries(properties).every(([prop, value]) => cookie[prop] === value) - && expiryFnc(cookie.expiry); + return ( + Object.entries(properties).every( + ([prop, value]) => cookie[prop] === value + ) && expiryFnc(cookie.expiry) + ); }; - return inputs.slice(0, 3).every(input => catchErrorTest(input, e => e instanceof TypeError)) + return ( + inputs + .slice(0, 3) + .every(input => + catchErrorTest(input, e => e instanceof TypeError) + ) && + // cookies[4] is the test case that is ignored for now - // cookies[4] is the test case that is ignored for now - - && catchErrorTypeMessageTest(inputs[5], CookieParseError, "Invalid cookie name \"\"!") - && catchErrorTypeMessageTest(inputs[6], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:28: GMT\"!") - && catchErrorTypeMessageTest(inputs[7], CookieParseError, "Invalid value for Expires \"Wed, 21 Onv 2015 07:28:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[8], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 20151 07:28:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[9], CookieParseError, "Invalid value for Expires \"Wed, 32 Oct 2015 07:28:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[10], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 25:28:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[11], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:61:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[12], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:28:00 UTC\"!") - && catchErrorTypeMessageTest(inputs[13], CookieParseError, "Invalid value for Expires \"Wed, 21 Oct 2015 07:28:00 GMT+2\"!") - && catchErrorTypeMessageTest(inputs[14], CookieParseError, "Invalid value for Expires \"San, 21 Onv 2015 07:28:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[15], CookieParseError, "Invalid value for Expires \"Wed, 31 Dec 1969 07:28:00 GMT\"!") - && catchErrorTypeMessageTest(inputs[16], CookieParseError, "Invalid value for Max-Age \"121252a\"!") - && catchErrorTypeMessageTest(inputs[17], CookieParseError, "Invalid key \"secur\" specified!") - && catchErrorTypeMessageTest(inputs[18], CookieParseError, "Invalid key \"httpoly\" with value \"2\" specified!") - - && inputs.slice(19, 21).every(input => catchErrorTypeMessageTest(input, CookieParseError, "Cookie has \"__Secure-\" prefix but \"Secure\" isn't set or the cookie is not set via https!")) - - && inputs.slice(21, 26).every(input => catchErrorTypeMessageTest(input, CookieParseError, "Cookie has \"__Host-\" prefix but \"Secure\" isn't set, the cookie is not set via https, \"Domain\" is set or \"Path\" is not equal to \"/\"!")) - - && catchErrorTypeMessageTest(inputs[26], CookieParseError, "Invalid value for Domain \"github.com\": cookie was received from \"gist.github.com\"!") - && catchErrorTypeMessageTest(inputs[27], CookieParseError, "Invalid value for Domain \"npmjs.com\": cookie was received from \"gist.github.com\"!") - - && compareCookieProps( - inputs[28], - exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), - { - name: "__Secure-id", - value: "a3fWa", - secure: true, - domain: "github.com", - subdomains: false, - path: "/" - } - ) - - && compareCookieProps( - inputs[29], - exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), - { - name: "__Host-id", - value: "a3fWa", - secure: true, - domain: "github.com", - subdomains: false, - path: "/" - } - ) - - && compareCookieProps( - inputs[30], - exp => exp.getTime() === new Date("Wed, 21 Nov 2099 20:28:33 GMT").getTime(), - { - name: "__Host-id", - value: "a3fWa", - secure: true, - domain: "github.com", - subdomains: false, - path: "/" - } - ) - - && compareCookieProps( - inputs[31], - exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), - { - name: "id", - value: "a3fWa", - secure: true, - domain: "usercontent.github.com", - subdomains: true, - path: "/lul/" - } - ) - - && compareCookieProps( - inputs[32], - exp => exp.getTime() === new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), - { - name: "id", - value: "a3fWa", - secure: true, - domain: "usercontent.github.com", - subdomains: true, - path: "/lul/" - } - ) - - && compareCookieProps( - inputs[33], - exp => exp.getTime() > new Date("Thu, 01 Jan 1970 00:00:00 GMT").getTime(), - { - name: "id", - value: "a3fWa", - secure: false, - domain: "github.com", - subdomains: false, - path: "/" - } - ) - - && compareCookieProps( - inputs[34], - exp => exp.getTime() > new Date("Thu, 01 Jan 1970 00:00:00 GMT").getTime(), - { - name: "id", - value: "a3fWa", - secure: false, - domain: "github.com", - subdomains: false, - path: "/" - } + catchErrorTypeMessageTest( + inputs[5], + CookieParseError, + 'Invalid cookie name ""!' + ) && + catchErrorTypeMessageTest( + inputs[6], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Oct 2015 07:28: GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[7], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Onv 2015 07:28:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[8], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Oct 20151 07:28:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[9], + CookieParseError, + 'Invalid value for Expires "Wed, 32 Oct 2015 07:28:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[10], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Oct 2015 25:28:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[11], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Oct 2015 07:61:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[12], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Oct 2015 07:28:00 UTC"!' + ) && + catchErrorTypeMessageTest( + inputs[13], + CookieParseError, + 'Invalid value for Expires "Wed, 21 Oct 2015 07:28:00 GMT+2"!' + ) && + catchErrorTypeMessageTest( + inputs[14], + CookieParseError, + 'Invalid value for Expires "San, 21 Onv 2015 07:28:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[15], + CookieParseError, + 'Invalid value for Expires "Wed, 31 Dec 1969 07:28:00 GMT"!' + ) && + catchErrorTypeMessageTest( + inputs[16], + CookieParseError, + 'Invalid value for Max-Age "121252a"!' + ) && + catchErrorTypeMessageTest( + inputs[17], + CookieParseError, + 'Invalid key "secur" specified!' + ) && + catchErrorTypeMessageTest( + inputs[18], + CookieParseError, + 'Invalid key "httpoly" with value "2" specified!' + ) && + inputs + .slice(19, 21) + .every(input => + catchErrorTypeMessageTest( + input, + CookieParseError, + 'Cookie has "__Secure-" prefix but "Secure" isn\'t set or the cookie is not set via https!' + ) + ) && + inputs + .slice(21, 26) + .every(input => + catchErrorTypeMessageTest( + input, + CookieParseError, + 'Cookie has "__Host-" prefix but "Secure" isn\'t set, the cookie is not set via https, "Domain" is set or "Path" is not equal to "/"!' + ) + ) && + catchErrorTypeMessageTest( + inputs[26], + CookieParseError, + 'Invalid value for Domain "github.com": cookie was received from "gist.github.com"!' + ) && + catchErrorTypeMessageTest( + inputs[27], + CookieParseError, + 'Invalid value for Domain "npmjs.com": cookie was received from "gist.github.com"!' + ) && + compareCookieProps( + inputs[28], + exp => + exp.getTime() === + new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "__Secure-id", + value: "a3fWa", + secure: true, + domain: "github.com", + subdomains: false, + path: "/" + } + ) && + compareCookieProps( + inputs[29], + exp => + exp.getTime() === + new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "__Host-id", + value: "a3fWa", + secure: true, + domain: "github.com", + subdomains: false, + path: "/" + } + ) && + compareCookieProps( + inputs[30], + exp => + exp.getTime() === + new Date("Wed, 21 Nov 2099 20:28:33 GMT").getTime(), + { + name: "__Host-id", + value: "a3fWa", + secure: true, + domain: "github.com", + subdomains: false, + path: "/" + } + ) && + compareCookieProps( + inputs[31], + exp => + exp.getTime() === + new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: true, + domain: "usercontent.github.com", + subdomains: true, + path: "/lul/" + } + ) && + compareCookieProps( + inputs[32], + exp => + exp.getTime() === + new Date("Wed, 21 Nov 2015 07:28:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: true, + domain: "usercontent.github.com", + subdomains: true, + path: "/lul/" + } + ) && + compareCookieProps( + inputs[33], + exp => + exp.getTime() > + new Date("Thu, 01 Jan 1970 00:00:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: false, + domain: "github.com", + subdomains: false, + path: "/" + } + ) && + compareCookieProps( + inputs[34], + exp => + exp.getTime() > + new Date("Thu, 01 Jan 1970 00:00:00 GMT").getTime(), + { + name: "id", + value: "a3fWa", + secure: false, + domain: "github.com", + subdomains: false, + path: "/" + } + ) ); }), new Test("static Cookie.fromObject()", () => { @@ -307,24 +437,34 @@ export default Test => [ const cookies = inputs.map(i => Cookie.fromObject(i)); - return cookies[0].name === "testname" - && cookies[0].value === "somevalue" - && cookies[0].domain === "github.com" - && cookies[0].path === "/" - && cookies[0].expiry.getTime() === new Date(inputs[0].expiry).getTime() - && cookies[0].secure === false - && cookies[0].subdomains === false - - && cookies[1].name === "testname" - && cookies[1].value === "lul" - && cookies[1].domain === "somedomain.tld" - && cookies[1].path === "/lel/" - && cookies[1].expiry.getTime() === date.getTime() - && cookies[1].secure === true - && cookies[1].subdomains === true; + return ( + cookies[0].name === "testname" && + cookies[0].value === "somevalue" && + cookies[0].domain === "github.com" && + cookies[0].path === "/" && + cookies[0].expiry.getTime() === + new Date(inputs[0].expiry).getTime() && + cookies[0].secure === false && + cookies[0].subdomains === false && + cookies[1].name === "testname" && + cookies[1].value === "lul" && + cookies[1].domain === "somedomain.tld" && + cookies[1].path === "/lel/" && + cookies[1].expiry.getTime() === date.getTime() && + cookies[1].secure === true && + cookies[1].subdomains === true + ); }), new Test("Cookie.serialize()", () => { - return new Cookie("abc=def; Expires=Wed, 20 Oct 2018 08:08:08 GMT; Path=/", "https://somedomain.tld").serialize() === "abc=def" - && new Cookie("jkhsd231=ajkshdgi", "https://somedomain.tld").serialize() === "jkhsd231=ajkshdgi"; + return ( + new Cookie( + "abc=def; Expires=Wed, 20 Oct 2018 08:08:08 GMT; Path=/", + "https://somedomain.tld" + ).serialize() === "abc=def" && + new Cookie( + "jkhsd231=ajkshdgi", + "https://somedomain.tld" + ).serialize() === "jkhsd231=ajkshdgi" + ); }) ]; diff --git a/test/errors.mjs b/test/errors.mjs index 7129a2b..8fc7193 100644 --- a/test/errors.mjs +++ b/test/errors.mjs @@ -8,20 +8,32 @@ export default Test => [ const validTypes = ["lol", "lel", "lul", "lal"]; const errors = [ paramError(position, paramName, functionName, validTypes[0]), - paramError(position, paramName, functionName, validTypes.slice(0, 2)), + paramError( + position, + paramName, + functionName, + validTypes.slice(0, 2) + ), paramError(position, paramName, functionName, validTypes) ]; - return errors.every(e => e instanceof TypeError) - && errors.every(e => e.name === "TypeError") - && errors[0].message === "something parameter \"some_param\" passed to \"some_func\" is not of type \"lol\"!" - && errors[1].message === "something parameter \"some_param\" passed to \"some_func\" is not of type \"lol\" or \"lel\"!" - && errors[2].message === "something parameter \"some_param\" passed to \"some_func\" is not of type \"lol\", \"lel\", \"lul\" or \"lal\"!"; + return ( + errors.every(e => e instanceof TypeError) && + errors.every(e => e.name === "TypeError") && + errors[0].message === + 'something parameter "some_param" passed to "some_func" is not of type "lol"!' && + errors[1].message === + 'something parameter "some_param" passed to "some_func" is not of type "lol" or "lel"!' && + errors[2].message === + 'something parameter "some_param" passed to "some_func" is not of type "lol", "lel", "lul" or "lal"!' + ); }), new Test("class CookieParseError", () => { const message = "this is a test error"; const error = new CookieParseError(message); - return error instanceof CookieParseError - && error.name === "CookieParseError" - && error.message === message; + return ( + error instanceof CookieParseError && + error.name === "CookieParseError" && + error.message === message + ); }) ]; diff --git a/test/index.mjs b/test/index.mjs index e1d2c04..12d6ef7 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -3,43 +3,51 @@ import errors from "./errors.mjs"; class Test { constructor(name, fnc) { - this.name = name - this.fnc = fnc + this.name = name; + this.fnc = fnc; } async runTest() { return this.fnc(); } } -const tests = [ - cookie, - errors -].flatMap(t => t(Test)); +const tests = [cookie, errors].flatMap(t => t(Test)); (async () => { console.log("running tests..."); - const testResults = await Promise.all(tests.map(async t => { - try { - t.result = await t.runTest(); - if(t.result !== !!t.result) { + const testResults = await Promise.all( + tests.map(async t => { + try { + t.result = await t.runTest(); + if (t.result !== !!t.result) { + t.result = false; + console.error("test did not return a boolean: " + t.name); + } + } catch (error) { + console.error( + "uncaught error in test: " + t.name + "\n", + error + ); t.result = false; - console.error("test did not return a boolean: " + t.name); } - } - catch(error) { - console.error("uncaught error in test: " + t.name + "\n", error); - t.result = false; - } - return t; - })); + return t; + }) + ); testResults.forEach(t => { - if(t.result) - console.info("✔ " + t.name); - else - console.warn("✘ " + t.name); + if (t.result) console.info("✔ " + t.name); + else console.warn("✘ " + t.name); }); - const succeededTests = testResults.map(t => t.result).reduce((a, b) => a + b); + const succeededTests = testResults + .map(t => t.result) + .reduce((a, b) => a + b); const success = succeededTests === testResults.length; - (success ? console.info : console.warn)((success ? "✔" : "✘") + " " + succeededTests + "/" + testResults.length + " tests successful"); + (success ? console.info : console.warn)( + (success ? "✔" : "✘") + + " " + + succeededTests + + "/" + + testResults.length + + " tests successful" + ); !success && process.exit(1); })();