From f040593110836eccb41a8ff9649b6a85a3c8bda6 Mon Sep 17 00:00:00 2001 From: ehmicky Date: Tue, 8 Dec 2020 19:14:47 +0100 Subject: [PATCH] Add eslint-config-node --- .editorconfig | 10 + .eslintrc.js | 40 ++ .eslintrc.json | 41 -- .gitattributes | 1 + .github/ISSUE_TEMPLATE.md | 2 +- .github/workflows/workflow.yml | 33 +- .prettierrc.json | 6 +- README.md | 61 +- ava.config.js | 6 - package-lock.json | 1070 ++++++++++++++++++++++++++++++++ package.json | 22 +- scripts/coverage.sh | 13 - src/addons.js | 18 +- src/deploy/hash-files.js | 16 +- src/deploy/hash-files.test.js | 2 +- src/deploy/hash-fns.js | 20 +- src/deploy/hash-fns.test.js | 2 +- src/deploy/hasher-segments.js | 12 +- src/deploy/index.js | 58 +- src/deploy/upload-files.js | 14 +- src/index.js | 26 +- src/index.test.js | 312 +++++----- src/methods/index.js | 13 +- src/methods/response.js | 2 +- src/methods/retry.js | 4 +- src/operations.js | 8 +- 26 files changed, 1450 insertions(+), 362 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json create mode 100644 .gitattributes delete mode 100644 ava.config.js delete mode 100755 scripts/coverage.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a5f2abd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +max_line_length = 120 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..dc12d9e --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,40 @@ +const { overrides } = require('@netlify/eslint-config-node') + +module.exports = { + extends: '@netlify/eslint-config-node', + rules: { + // TODO: enable those rules + 'array-callback-return': 0, + complexity: 0, + 'consistent-this': 0, + 'func-names': 0, + 'func-style': 0, + 'id-length': 0, + 'line-comment-position': 0, + 'max-nested-callbacks': 0, + 'max-statements': 0, + 'no-await-in-loop': 0, + 'no-inline-comments': 0, + 'no-magic-numbers': 0, + 'no-param-reassign': 0, + 'no-promise-executor-return': 0, + 'no-shadow': 0, + 'fp/no-class': 0, + 'fp/no-delete': 0, + 'fp/no-let': 0, + 'fp/no-loops': 0, + 'fp/no-mutating-assign': 0, + 'fp/no-mutating-methods': 0, + 'fp/no-mutation': 0, + 'fp/no-this': 0, + 'node/exports-style': 0, + 'node/global-require': 0, + 'node/prefer-global/process': 0, + 'promise/no-callback-in-promise': 0, + 'promise/prefer-await-to-callbacks': 0, + 'promise/prefer-await-to-then': 0, + 'unicorn/filename-case': 0, + 'you-dont-need-lodash-underscore/flatten': 0, + }, + overrides: [...overrides], +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 5cee3d2..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "parser": "babel-eslint", - "plugins": ["node", "prettier"], - "env": { - "node": true, - "es6": true - }, - "extends": [ - "plugin:import/errors", - "plugin:import/warnings", - "eslint:recommended", - "plugin:node/recommended", - "prettier" - ], - "rules": { - "no-console": 0, - "no-unused-vars": 2, - "import/order": [ - 2, - { - "newlines-between": "always", - "alphabetize": { - "order": "asc", - "caseInsensitive": true - } - } - ], - "no-process-exit": 0, - "require-atomic-updates": 0, - "no-undef": [2, { "typeof": true }] - }, - "overrides": [ - { - "files": "**/*.test.js", - "rules": { - "node/no-unpublished-require": 0, - "node/no-missing-require": 0 - } - } - ] -} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4a02feb..a702460 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -16,7 +16,7 @@ Use the commands below to provide key information from your environment: You do NOT have to include this information if this is a FEATURE REQUEST --> -**- Do you want to request a *feature* or report a *bug*?** +**- Do you want to request a _feature_ or report a _bug_?** **- What is the current behavior?** diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 0a79bbb..5aca9f5 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -6,21 +6,20 @@ on: tags: ['*'] pull_request: types: [opened, synchronize, reopened] -env: - CI: 'true' jobs: build: + runs-on: ${{ matrix.os }} + timeout-minutes: 30 strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - node-version: [8.3.0, 14] + node-version: [8.17.0, 14.x] exclude: - os: macOS-latest - node-version: 8.3.0 + node-version: 8.17.0 - os: windows-latest - node-version: 8.3.0 + node-version: 8.17.0 fail-fast: false - runs-on: ${{ matrix.os }} steps: - name: Git checkout uses: actions/checkout@v2 @@ -29,14 +28,24 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: Install dependencies - run: npm install + run: npm ci - name: Linting - run: npm run format - if: "${{ matrix.node-version == '14' }}" + run: npm run format:ci + if: "${{ matrix.node-version == '14.x' }}" - name: Tests run: npm run test:ci + - name: Get test coverage flags + id: test-coverage-flags + run: |- + os=${{ matrix.os }} + node=${{ matrix.node-version }} + echo "::set-output name=os::${os/-latest/}" + echo "::set-output name=node::node_${node//./}" + shell: bash + - uses: codecov/codecov-action@v1 + with: + file: coverage/coverage-final.json + flags: ${{ steps.test-coverage-flags.outputs.os }},${{ steps.test-coverage-flags.outputs.node }} - name: Build run: npm run build - if: "${{ matrix.node-version == '14' }}" - - name: Codecov test coverage - run: bash scripts/coverage.sh "${{ matrix.os }}" "${{ matrix.node-version }}" + if: "${{ matrix.node-version == '14.x' }}" diff --git a/.prettierrc.json b/.prettierrc.json index 31ba22d..c63bdb8 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1 @@ -{ - "semi": false, - "singleQuote": true, - "printWidth": 120 -} +"@netlify/eslint-config-node/.prettierrc.json" diff --git a/README.md b/README.md index a29b26b..1b3a66e 100644 --- a/README.md +++ b/README.md @@ -9,31 +9,38 @@ A Netlify [OpenAPI](https://github.com/netlify/open-api) client that works in th ```js const NetlifyAPI = require('netlify') -const client = new NetlifyAPI('1234myAccessToken') -const sites = await client.listSites() + +const listNetlifySites = async function() { + const client = new NetlifyAPI('1234myAccessToken') + const sites = await client.listSites() + return sites +} ``` ## Using OpenAPI operations ```js const NetlifyAPI = require('netlify') + const client = new NetlifyAPI('1234myAccessToken') -// Fetch sites -const sites = await client.listSites() +const listCreateAndDeleteSite = async function() { + // Fetch sites + const sites = await client.listSites() -// Create a site. Notice `body` here for sending OpenAPI body -const site = await client.createSite({ - body: { - name: `my-awesome-site`, - // ... https://open-api.netlify.com/#/default/createSite - }, -}) + // Create a site. Notice `body` here for sending OpenAPI body + const site = await client.createSite({ + body: { + name: `my-awesome-site`, + // ... https://open-api.netlify.com/#/default/createSite + }, + }) -// Delete site. Notice `site_id` is a path parameter https://open-api.netlify.com/#/default/deleteSite -await client.deleteSite({ - site_id: siteId, -}) + // Delete site. Notice `site_id` is a path parameter https://open-api.netlify.com/#/default/deleteSite + await client.deleteSite({ + site_id: siteId, + }) +} ``` ## API @@ -47,7 +54,7 @@ Create a new instance of the Netlify API client with the provided `accessToken`. `opts` includes: ```js -{ +const opts = { userAgent: 'netlify/js-client', scheme: 'https', host: 'api.netlify.com', @@ -83,7 +90,7 @@ Performs a call to the given endpoint corresponding with the `operationId`. Retu ```js // example params -{ +const params = { any_param_needed, paramsCanAlsoBeCamelCase, body: { @@ -96,7 +103,7 @@ Optional `opts` can include any property you want passed to [`node-fetch`](https ```js // example opts -{ +const opts = { headers: { // Default headers 'User-agent': 'netlify-js-client', accept: 'application/json' @@ -115,7 +122,7 @@ async function getSomeData() { siteId: '1234abcd', deploy_id: '4567', }) - } catch (e) { + } catch (error) { // handle error } } @@ -134,7 +141,7 @@ Pass in a [`ticket`](https://open-api.netlify.com/#model-ticket) and get back an Optional `opts` include: ```js -{ +const opts = { poll: 1000, // number of ms to wait between polling timeout: 3.6e6 // number of ms to wait before timing out } @@ -169,7 +176,7 @@ The following paths can be passed in the options: Optional `opts` include: ```js -{ +const opts = { fnDir: null, // path to a folder of functions to deploy branch: null, // branch to pass onto the netlify api configPath: null, // path to a netlify.toml file to include in the deploy (e.g. redirect support for manual deploys) @@ -183,17 +190,17 @@ Optional `opts` include: tmpDir: tempy.directory(), // a temporary directory to zip functions into statusCb: statusObj => { // a callback function to receive status events - /* statusObj: { - type: name-of-step - msg: msg to print - phase: [start, progress, stop] - } */ + // statusObj: { + // type: name-of-step + // msg: msg to print + // phase: [start, progress, stop] + // } // See https://github.com/netlify/cli/blob/v2.0.0-beta.3/src/commands/deploy.js#L161-L195 // for an example of how this can be used. }, // passing a deployId will update an existing deploy based on the provided options deployId: null - } +} ``` ## Proxy support diff --git a/ava.config.js b/ava.config.js deleted file mode 100644 index 07e2559..0000000 --- a/ava.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - files: ['src/**/*.test.js'], - compileEnhancements: false, - babel: false, - verbose: true, -} diff --git a/package-lock.json b/package-lock.json index c6e7c2c..bca3167 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1329,6 +1329,117 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@netlify/eslint-config-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@netlify/eslint-config-node/-/eslint-config-node-2.0.1.tgz", + "integrity": "sha512-kx/FXuXQLa5SNJfb3bQZN19F6MRVqvpvX8CBDOTeojoL11yAChqzREMQkDsZJ3t8PgIcxZF674lGPEYUaVFV5w==", + "dev": true, + "requires": { + "babel-eslint": "^10.1.0", + "cross-env": "^7.0.2", + "eslint": "^7.11.0", + "eslint-config-prettier": "^7.0.0", + "eslint-config-standard": "^16.0.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-plugin-ava": "^11.0.0", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-fp": "^2.3.0", + "eslint-plugin-html": "^6.1.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-markdown": "^1.0.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-unicorn": "^23.0.0", + "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0", + "execa": "^5.0.0", + "husky": "^4.3.0", + "npm-run-all": "^4.1.5", + "prettier": "^2.1.2" + }, + "dependencies": { + "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" + } + }, + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "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 + }, + "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 + }, + "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" + } + } + } + }, "@netlify/open-api": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-0.18.1.tgz", @@ -1435,6 +1546,12 @@ "integrity": "sha512-33/L34xS7HVUx23e0wOT2V1qPF1IrHgQccdJVm9uXGTB9vFBrrzBtkQymT8VskeKOxjz55MSqMv0xuLq+u98WQ==", "dev": true }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -1998,6 +2115,40 @@ "es-abstract": "^1.17.0-next.1" } }, + "array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", @@ -2346,6 +2497,12 @@ "precond": "0.2" } }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2649,6 +2806,12 @@ "node-releases": "^1.1.66" } }, + "buf-compare": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buf-compare/-/buf-compare-1.0.1.tgz", + "integrity": "sha1-/vKNqLgROgoNtEMLC2Rntpcws0o=", + "dev": true + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2791,6 +2954,16 @@ "write-file-atomic": "^3.0.0" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "call-matcher": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/call-matcher/-/call-matcher-1.1.0.tgz", @@ -2875,6 +3048,24 @@ "remove-markdown": "^0.2.2" } }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -2973,6 +3164,15 @@ "lodash.transform": "^4.6.0" } }, + "clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha1-jffHquUf02h06PjQW5GAvBGj/tc=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3061,6 +3261,12 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, + "collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -3354,6 +3560,16 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-assert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/core-assert/-/core-assert-0.2.1.tgz", + "integrity": "sha1-+F4s+b/tKPdzzIs/pcW2m9wC/j8=", + "dev": true, + "requires": { + "buf-compare": "^1.0.0", + "is-error": "^2.2.0" + } + }, "core-js": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", @@ -3456,6 +3672,15 @@ } } }, + "create-eslint-index": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/create-eslint-index/-/create-eslint-index-1.0.0.tgz", + "integrity": "sha1-2VQ3LYbVeS/NZ+nyt5GxqxYkEbs=", + "dev": true, + "requires": { + "lodash.get": "^4.3.0" + } + }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -3483,6 +3708,58 @@ "sha.js": "^2.4.8" } }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + }, + "dependencies": { + "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" + } + }, + "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 + }, + "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 + }, + "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" + } + } + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -3612,6 +3889,15 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "deep-strict-equal": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deep-strict-equal/-/deep-strict-equal-0.2.0.tgz", + "integrity": "sha1-SgeBR6irV/ag1PVUckPNIvROtOQ=", + "dev": true, + "requires": { + "core-assert": "^0.2.0" + } + }, "default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -3900,12 +4186,71 @@ "esutils": "^2.0.2" } }, + "dom-serializer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "dev": true, + "requires": { + "domelementtype": "^2.1.0" + } + } + } + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true + }, + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "dev": true, + "requires": { + "domelementtype": "^2.1.0" + } + } + } + }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -4002,6 +4347,15 @@ "once": "^1.4.0" } }, + "enhance-visitors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/enhance-visitors/-/enhance-visitors-1.0.0.tgz", + "integrity": "sha1-qpRdBdpGVnKh69OP7i7T2oUY6Vo=", + "dev": true, + "requires": { + "lodash": "^4.13.1" + } + }, "enhanced-resolve": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", @@ -4049,6 +4403,12 @@ "ansi-colors": "^4.1.1" } }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true + }, "envinfo": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", @@ -4347,12 +4707,28 @@ } } }, + "eslint-ast-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz", + "integrity": "sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==", + "dev": true, + "requires": { + "lodash.get": "^4.4.2", + "lodash.zip": "^4.2.0" + } + }, "eslint-config-prettier": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.0.0.tgz", "integrity": "sha512-8Y8lGLVPPZdaNA7JXqnvETVC7IiVRgAP6afQu9gOQRn90YY3otMNh+x7Vr2vMePQntF+5erdSUBqSzCmU/AxaQ==", "dev": true }, + "eslint-config-standard": { + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.2.tgz", + "integrity": "sha512-fx3f1rJDsl9bY7qzyX8SAtP8GBSk6MfXFaTfaGgk12aAYW4gJSyRm7dM790L6cbXv63fvjY4XeSzXnb4WM+SKw==", + "dev": true + }, "eslint-import-resolver-node": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", @@ -4475,6 +4851,31 @@ } } }, + "eslint-plugin-ava": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-11.0.0.tgz", + "integrity": "sha512-UMGedfl/gIKx1tzjGtAsTSJgowyAEZU2VWmpoWXYcuuV4B2H4Cu90yuMgMPEVt1mQlIZ21L7YM2CSpHUFJo/LQ==", + "dev": true, + "requires": { + "deep-strict-equal": "^0.2.0", + "enhance-visitors": "^1.0.0", + "eslint-utils": "^2.1.0", + "espree": "^7.2.0", + "espurify": "^2.0.1", + "import-modules": "^2.0.0", + "micro-spelling-correcter": "^1.1.1", + "pkg-dir": "^4.2.0", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "espurify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/espurify/-/espurify-2.0.1.tgz", + "integrity": "sha512-7w/dUrReI/QbJFHRwfomTlkQOXaB1NuCrBRn5Y26HXn5gvh18/19AgLbayVrNxXQfkckvgrJloWyvZDuJ7dhEA==", + "dev": true + } + } + }, "eslint-plugin-es": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", @@ -4502,6 +4903,37 @@ } } }, + "eslint-plugin-eslint-comments": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", + "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "ignore": "^5.0.5" + } + }, + "eslint-plugin-fp": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-fp/-/eslint-plugin-fp-2.3.0.tgz", + "integrity": "sha1-N20qEIcQ6YGYC9w4deO5kg2gSJw=", + "dev": true, + "requires": { + "create-eslint-index": "^1.0.0", + "eslint-ast-utils": "^1.0.0", + "lodash": "^4.13.1", + "req-all": "^0.1.0" + } + }, + "eslint-plugin-html": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-6.1.1.tgz", + "integrity": "sha512-JSe3ZDb7feKMnQM27XWGeoIjvP4oWQMJD9GZ6wW67J7/plVL87NK72RBwlvfc3tTZiYUchHhxAwtgEd1GdofDA==", + "dev": true, + "requires": { + "htmlparser2": "^5.0.1" + } + }, "eslint-plugin-import": { "version": "2.22.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", @@ -4666,6 +5098,17 @@ } } }, + "eslint-plugin-markdown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-1.0.2.tgz", + "integrity": "sha512-BfvXKsO0K+zvdarNc801jsE/NTLmig4oKhZ1U3aSUgTf2dB/US5+CrfGxMsCK2Ki1vS1R3HPok+uYpufFndhzw==", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "remark-parse": "^5.0.0", + "unified": "^6.1.2" + } + }, "eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -4716,6 +5159,145 @@ "prettier-linter-helpers": "^1.0.0" } }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", + "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flatmap": "^1.2.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "object.entries": "^1.1.2", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.18.1", + "string.prototype.matchall": "^4.0.2" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-unicorn": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-23.0.0.tgz", + "integrity": "sha512-Vabo3cjl6cjyhcf+76CdQEY6suOFzK0Xh3xo0uL9VDYrDJP5+B6PjV0tHTYm82WZmFWniugFJM3ywHSNYTi/ZQ==", + "dev": true, + "requires": { + "ci-info": "^2.0.0", + "clean-regexp": "^1.0.0", + "eslint-ast-utils": "^1.1.0", + "eslint-template-visitor": "^2.2.1", + "eslint-utils": "^2.1.0", + "import-modules": "^2.0.0", + "lodash": "^4.17.20", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.21", + "reserved-words": "^0.1.2", + "safe-regex": "^2.1.1", + "semver": "^7.3.2" + }, + "dependencies": { + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "requires": { + "regexp-tree": "~0.1.1" + } + } + } + }, + "eslint-plugin-you-dont-need-lodash-underscore": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-you-dont-need-lodash-underscore/-/eslint-plugin-you-dont-need-lodash-underscore-6.10.0.tgz", + "integrity": "sha512-Zu1KbHiWKf+alVvT+kFX2M5HW1gmtnkfF1l2cjmFozMnG0gbGgXo8oqK7lwk+ygeOXDmVfOyijqBd7SUub9AEQ==", + "dev": true, + "requires": { + "kebab-case": "^1.0.0" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -4726,6 +5308,18 @@ "estraverse": "^4.1.1" } }, + "eslint-template-visitor": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/eslint-template-visitor/-/eslint-template-visitor-2.2.1.tgz", + "integrity": "sha512-q3SxoBXz0XjPGkUpwGVAwIwIPIxzCAJX1uwfVc8tW3v7u/zS7WXNH3I2Mu2MDz2NgSITAyKLRaQFPHu/iyKxDQ==", + "dev": true, + "requires": { + "babel-eslint": "^10.1.0", + "eslint-visitor-keys": "^1.3.0", + "esquery": "^1.3.1", + "multimap": "^1.1.0" + } + }, "eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -4924,6 +5518,12 @@ } } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -5495,6 +6095,17 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -6203,6 +6814,18 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + } + }, "http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -6356,6 +6979,12 @@ "resolve-cwd": "^3.0.0" } }, + "import-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-modules/-/import-modules-2.0.0.tgz", + "integrity": "sha512-iczM/v9drffdNnABOKwj0f9G3cFDon99VcG1mxeBsdqnbd+vnQ5c2uAiCHNQITqFTOPaEvwg3VjoWCur0uHLEw==", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6470,6 +7099,17 @@ } } }, + "internal-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", + "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", + "dev": true, + "requires": { + "es-abstract": "^1.17.0-next.1", + "has": "^1.0.3", + "side-channel": "^1.0.2" + } + }, "interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -6502,6 +7142,22 @@ } } }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", @@ -6578,6 +7234,12 @@ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -6627,6 +7289,12 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true + }, "is-installed-globally": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", @@ -6772,12 +7440,24 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, + "is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "dev": true + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", + "dev": true + }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -7028,11 +7708,27 @@ "minimist": "^1.2.5" } }, + "jsx-ast-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", + "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.1" + } + }, "junk": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==" }, + "kebab-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.0.tgz", + "integrity": "sha1-P55JkK3K0MaGwOcB92RYaPdfkes=", + "dev": true + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -7249,6 +7945,12 @@ "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", "dev": true }, + "lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", + "dev": true + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -7258,6 +7960,15 @@ "chalk": "^2.0.1" } }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "loud-rejection": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.2.0.tgz", @@ -7320,6 +8031,12 @@ "object-visit": "^1.0.0" } }, + "markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "dev": true + }, "matcher": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz", @@ -7457,6 +8174,12 @@ "resolved": "https://registry.npmjs.org/micro-api-client/-/micro-api-client-3.3.0.tgz", "integrity": "sha512-y0y6CUB9RLVsy3kfgayU28746QrNMpSm9O/AYGNsBgOkJr/X/Jk0VLGoO8Ude7Bpa8adywzF+MzXNZRFRsNPhg==" }, + "micro-spelling-correcter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", + "integrity": "sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg==", + "dev": true + }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", @@ -7704,6 +8427,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "multimap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz", + "integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==", + "dev": true + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -8131,6 +8860,74 @@ } } }, + "object.entries": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "object.fromentries": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.3.tgz", + "integrity": "sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -8425,6 +9222,20 @@ "safe-buffer": "^5.1.1" } }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "parse-github-url": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", @@ -8636,6 +9447,12 @@ "irregular-plurals": "^2.0.0" } }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -8764,6 +9581,17 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "propagate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", @@ -8893,6 +9721,12 @@ "strip-json-comments": "~2.0.1" } }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -9104,6 +9938,12 @@ "safe-regex": "^1.1.0" } }, + "regexp-tree": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.21.tgz", + "integrity": "sha512-kUUXjX4AnqnR8KRTCrayAo9PzYMRKmVoGgaz2tBuz0MF3g1ZbGebmtW0yFHfFK9CmBjQKeYIgoL22pFLBJY7sw==", + "dev": true + }, "regexp.prototype.flags": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", @@ -9184,6 +10024,29 @@ "es6-error": "^4.0.1" } }, + "remark-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", + "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "dev": true, + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + } + }, "remove-markdown": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.2.2.tgz", @@ -9207,6 +10070,18 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "req-all": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/req-all/-/req-all-0.1.0.tgz", + "integrity": "sha1-EwBR4qzligLqy/ydRIV3pzapJzo=", + "dev": true + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9228,6 +10103,12 @@ "integrity": "sha1-WhtS63Dr7UPrmC6XTIWrWVceVvo=", "dev": true }, + "reserved-words": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", + "integrity": "sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE=", + "dev": true + }, "resolve": { "version": "2.0.0-next.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.2.tgz", @@ -9502,6 +10383,38 @@ "rechoir": "^0.6.2" } }, + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "dev": true, + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -9863,6 +10776,12 @@ "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", "dev": true }, + "state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", + "dev": true + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -9967,6 +10886,43 @@ "strip-ansi": "^6.0.0" } }, + "string.prototype.matchall": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", + "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "string.prototype.padend": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", @@ -10472,6 +11428,12 @@ "is-number": "^7.0.0" } }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, "trim-newlines": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", @@ -10490,6 +11452,18 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "dev": true + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true + }, "tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -10584,6 +11558,16 @@ "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=", "dev": true }, + "unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dev": true, + "requires": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -10612,6 +11596,20 @@ "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", "dev": true }, + "unified": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", + "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "dev": true, + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^2.0.0", + "x-is-string": "^0.1.0" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -10666,6 +11664,45 @@ "uid2": "0.0.3" } }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-remove-position": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", + "dev": true, + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", + "dev": true + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + }, "unixify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", @@ -10849,6 +11886,33 @@ "spdx-expression-parse": "^3.0.0" } }, + "vfile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", + "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "dev": true, + "requires": { + "is-buffer": "^1.1.4", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-location": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", + "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", + "dev": true + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "dev": true, + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -11652,6 +12716,12 @@ } } }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", + "dev": true + }, "xdg-basedir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", diff --git a/package.json b/package.json index 71f602b..5842e07 100644 --- a/package.json +++ b/package.json @@ -17,15 +17,26 @@ "scripts": { "prepublishOnly": "npm run build && git push && git push --tags && gh-release", "test": "npm run format && npm run test:dev", - "format": "run-s format:*", + "format": "run-s format:check-fix:*", "format:lint": "eslint --ignore-path .gitignore --fix --cache --format=codeframe --max-warnings=0 \"src/**/*.js\"", "format:prettier": "prettier --ignore-path .gitignore --write --loglevel warn \"src/**/*.js\" \"*.{js,md,yml,json}\" \"!package-lock.json\"", + "format:ci": "run-s format:check:*", + "format:check-fix:lint": "run-e format:check:lint format:fix:lint", + "format:check:lint": "cross-env-shell eslint $npm_package_scriptsArgs_eslint", + "format:fix:lint": "cross-env-shell eslint --fix $npm_package_scriptsArgs_eslint", + "format:check-fix:prettier": "run-e format:check:prettier format:fix:prettier", + "format:check:prettier": "cross-env-shell prettier --check $npm_package_scriptsArgs_prettier", + "format:fix:prettier": "cross-env-shell prettier --write $npm_package_scriptsArgs_prettier", "test:dev": "ava", "test:ci": "nyc -r lcovonly -r text -r json ava", "update-snapshots": "ava -u", "build": "webpack", "version": "auto-changelog -p --template keepachangelog --breaking-pattern breaking && npm run format:prettier && git add CHANGELOG.md" }, + "scriptsArgs": { + "eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{src,tests,.github}/**/*.{js,md,html}\" \"*.{js,md,html}\" \".*.{js,md,html}\"", + "prettier": "--ignore-path .gitignore --loglevel=warn \"{src,tests,.github}/**/*.{js,md,yml,json,html}\" \"*.{js,yml,json,html}\" \".*.{js,yml,json,html}\" \"!package-lock.json\"" + }, "husky": { "hooks": { "pre-push": "npm run format" @@ -84,6 +95,7 @@ "@babel/plugin-transform-runtime": "^7.9.0", "@babel/preset-env": "^7.9.0", "@babel/runtime": "^7.9.2", + "@netlify/eslint-config-node": "^2.0.1", "auto-changelog": "^2.0.0", "ava": "^2.4.0", "babel-eslint": "^10.1.0", @@ -106,5 +118,13 @@ }, "engines": { "node": ">=8.3.0" + }, + "ava": { + "files": [ + "src/**/*.test.js" + ], + "compileEnhancements": false, + "babel": false, + "verbose": true } } diff --git a/scripts/coverage.sh b/scripts/coverage.sh deleted file mode 100755 index ca26e7e..0000000 --- a/scripts/coverage.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# Upload test coverage to Codecov - -os="$1" -os="${os/-latest/}" - -node="$2" -node="node_${node//./_}" - -# We don't use the -Z flag (which makes CI build fail on upload error) because -# Codecov fails wait too often. -curl --fail -s https://codecov.io/bash | \ - bash -s -- -f coverage/coverage-final.json -F "$os" -F "$node" diff --git a/src/addons.js b/src/addons.js index 5c2d38f..4f4ca6d 100644 --- a/src/addons.js +++ b/src/addons.js @@ -1,6 +1,6 @@ -/* temp api until endpoints in openAPI spec - TODO update openAPI spec and update `addons` commands -*/ +// temp api until endpoints in openAPI spec +// TODO update openAPI spec and update `addons` commands +// const fetch = require('node-fetch') async function createAddon(settings, netlifyApiToken) { @@ -13,7 +13,7 @@ async function createAddon(settings, netlifyApiToken) { Authorization: `Bearer ${netlifyApiToken}`, }, body: JSON.stringify({ - config: config, + config, }), }) @@ -75,7 +75,7 @@ async function updateAddon(settings, netlifyApiToken) { Authorization: `Bearer ${netlifyApiToken}`, }, body: JSON.stringify({ - config: config, + config, }), }) @@ -88,8 +88,8 @@ async function updateAddon(settings, netlifyApiToken) { } module.exports = { - getAddons: getAddons, - createAddon: createAddon, - updateAddon: updateAddon, - deleteAddon: deleteAddon, + getAddons, + createAddon, + updateAddon, + deleteAddon, } diff --git a/src/deploy/hash-files.js b/src/deploy/hash-files.js index 8a7913d..118b9b6 100644 --- a/src/deploy/hash-files.js +++ b/src/deploy/hash-files.js @@ -7,14 +7,12 @@ const { hasherCtor, manifestCollectorCtor, fileFilterCtor, fileNormalizerCtor } module.exports = hashFiles async function hashFiles(dir, configPath, opts) { - opts = Object.assign( - { - concurrentHash: 100, - assetType: 'file', - statusCb: () => {}, - }, - opts - ) + opts = { + concurrentHash: 100, + assetType: 'file', + statusCb: () => {}, + ...opts, + } if (!opts.filter) throw new Error('Missing filter function option') const fileStream = walker([configPath, dir], { filter: opts.filter }) @@ -24,7 +22,7 @@ async function hashFiles(dir, configPath, opts) { // Written to by manifestCollector const files = {} // normalizedPath: hash (wanted by deploy API) - const filesShaMap = {} //hash: [fileObj, fileObj, fileObj] + const filesShaMap = {} // hash: [fileObj, fileObj, fileObj] const manifestCollector = manifestCollectorCtor(files, filesShaMap, opts) await pump(fileStream, filter, hasher, fileNormalizer, manifestCollector) diff --git a/src/deploy/hash-files.test.js b/src/deploy/hash-files.test.js index b0b6e31..9b8f5c6 100644 --- a/src/deploy/hash-files.test.js +++ b/src/deploy/hash-files.test.js @@ -13,6 +13,6 @@ test('hashes files in a folder', async (t) => { Object.keys(files).forEach((path) => t.truthy(path, 'each file has a path')) t.truthy(filesShaMap, 'filesShaMap is returned') Object.values(filesShaMap).forEach((fileObjArray) => - fileObjArray.forEach((fileObj) => t.truthy(fileObj.normalizedPath, 'fileObj have a normalizedPath field')) + fileObjArray.forEach((fileObj) => t.truthy(fileObj.normalizedPath, 'fileObj have a normalizedPath field')), ) }) diff --git a/src/deploy/hash-fns.js b/src/deploy/hash-fns.js index 25df9c3..55bbcab 100644 --- a/src/deploy/hash-fns.js +++ b/src/deploy/hash-fns.js @@ -9,16 +9,14 @@ const { hasherCtor, manifestCollectorCtor } = require('./hasher-segments') module.exports = hashFns async function hashFns(dir, opts) { - opts = Object.assign( - { - concurrentHash: 100, - assetType: 'function', - hashAlgorithm: 'sha256', - // tmpDir, - statusCb: () => {}, - }, - opts - ) + opts = { + concurrentHash: 100, + assetType: 'function', + hashAlgorithm: 'sha256', + // tmpDir, + statusCb: () => {}, + ...opts, + } // early out if the functions dir is omitted if (!dir) return { functions: {}, shaMap: {} } if (!opts.tmpDir) throw new Error('Missing tmpDir directory for zipping files') @@ -43,7 +41,7 @@ async function hashFns(dir, opts) { // Written to by manifestCollector const functions = {} // normalizedPath: hash (wanted by deploy API) - const fnShaMap = {} //hash: [fileObj, fileObj, fileObj] + const fnShaMap = {} // hash: [fileObj, fileObj, fileObj] const manifestCollector = manifestCollectorCtor(functions, fnShaMap, opts) await pump(functionStream, hasher, manifestCollector) diff --git a/src/deploy/hash-fns.test.js b/src/deploy/hash-fns.test.js index 3356531..f5d34a6 100644 --- a/src/deploy/hash-fns.test.js +++ b/src/deploy/hash-fns.test.js @@ -10,6 +10,6 @@ test('hashes files in a folder', async (t) => { Object.keys(functions).forEach((path) => t.truthy(path, 'each file has a path')) t.truthy(fnShaMap, 'fnShaMap is returned') Object.values(fnShaMap).forEach((fileObjArray) => - fileObjArray.forEach((fileObj) => t.truthy(fileObj.normalizedPath, 'fileObj have a normalizedPath field')) + fileObjArray.forEach((fileObj) => t.truthy(fileObj.normalizedPath, 'fileObj have a normalizedPath field')), ) }) diff --git a/src/deploy/hasher-segments.js b/src/deploy/hasher-segments.js index f95c20f..056f45b 100644 --- a/src/deploy/hasher-segments.js +++ b/src/deploy/hasher-segments.js @@ -14,17 +14,15 @@ exports.hasherCtor = ({ concurrentHash, hashAlgorithm = 'sha1' }) => { hasha .fromFile(fileObj.filepath, hashaOpts) // insert hash and asset type to file obj - .then((hash) => cb(null, Object.assign({}, fileObj, { hash }))) - .catch((err) => cb(err)) + .then((hash) => cb(null, { ...fileObj, hash })) + .catch((error) => cb(error)) }) } // Inject normalized file names into normalizedPath and assetType exports.fileNormalizerCtor = fileNormalizerCtor function fileNormalizerCtor({ assetType = 'file' }) { - return map((fileObj) => { - return Object.assign({}, fileObj, { assetType, normalizedPath: normalizePath(fileObj.relname) }) - }) + return map((fileObj) => ({ ...fileObj, assetType, normalizedPath: normalizePath(fileObj.relname) })) } // A writable stream segment ctor that normalizes file paths, and writes shaMap's @@ -51,6 +49,4 @@ exports.manifestCollectorCtor = (filesObj, shaMap, { statusCb, assetType }) => { } // transform stream ctor that filters folder-walker results for only files -exports.fileFilterCtor = objFilterCtor((fileObj) => { - return fileObj.type === 'file' -}) +exports.fileFilterCtor = objFilterCtor((fileObj) => fileObj.type === 'file') diff --git a/src/deploy/index.js b/src/deploy/index.js index 159a37f..55ab023 100644 --- a/src/deploy/index.js +++ b/src/deploy/index.js @@ -11,33 +11,31 @@ const { waitForDiff } = require('./util') const { waitForDeploy, getUploadList, defaultFilter } = require('./util') module.exports = async (api, siteId, dir, opts) => { - opts = Object.assign( - { - fnDir: null, - configPath: null, - draft: false, - message: undefined, // API calls this the 'title' - tmpDir: tempy.directory(), - deployTimeout: 1.2e6, // local deploy timeout: 20 mins - concurrentHash: 100, // concurrent file hash calls - concurrentUpload: 5, // Number of concurrent uploads - filter: defaultFilter, - syncFileLimit: 100, // number of files - maxRetry: 5, // number of times to retry an upload - statusCb: () => { - /* default to noop */ - /* statusObj: { - type: name-of-step - msg: msg to print - phase: [start, progress, stop], - spinner: a spinner from cli-spinners package - } */ - }, - // allows updating an existing deploy - deployId: null, + opts = { + fnDir: null, + configPath: null, + draft: false, + message: undefined, // API calls this the 'title' + tmpDir: tempy.directory(), + deployTimeout: 1.2e6, // local deploy timeout: 20 mins + concurrentHash: 100, // concurrent file hash calls + concurrentUpload: 5, // Number of concurrent uploads + filter: defaultFilter, + syncFileLimit: 100, // number of files + maxRetry: 5, // number of times to retry an upload + statusCb: () => { + /* default to noop */ + // statusObj: { + // type: name-of-step + // msg: msg to print + // phase: [start, progress, stop], + // spinner: a spinner from cli-spinners package + // } }, - opts - ) + // allows updating an existing deploy + deployId: null, + ...opts, + } const { fnDir, configPath, statusCb, message: title } = opts @@ -57,7 +55,7 @@ module.exports = async (api, siteId, dir, opts) => { statusCb({ type: 'hashing', - msg: `Finished hashing ${filesCount} files` + (fnDir ? ` and ${functionsCount} functions` : ''), + msg: `Finished hashing ${filesCount} files${fnDir ? ` and ${functionsCount} functions` : ''}`, phase: 'stop', }) @@ -98,9 +96,9 @@ module.exports = async (api, siteId, dir, opts) => { statusCb({ type: 'create-deploy', - msg: - `CDN requesting ${requiredFiles.length} files` + - (Array.isArray(requiredFns) ? ` and ${requiredFns.length} functions` : ''), + msg: `CDN requesting ${requiredFiles.length} files${ + Array.isArray(requiredFns) ? ` and ${requiredFns.length} functions` : '' + }`, phase: 'stop', }) diff --git a/src/deploy/upload-files.js b/src/deploy/upload-files.js index c95a809..e3ff48a 100644 --- a/src/deploy/upload-files.js +++ b/src/deploy/upload-files.js @@ -31,7 +31,7 @@ async function uploadFiles(api, deployId, uploadList, { concurrentUpload, status deployId, path: encodeURI(normalizedPath), }), - maxRetry + maxRetry, ) break } @@ -44,7 +44,7 @@ async function uploadFiles(api, deployId, uploadList, { concurrentUpload, status name: encodeURI(normalizedPath), runtime, }), - maxRetry + maxRetry, ) break } @@ -91,15 +91,15 @@ function retryUpload(uploadFn, maxRetry) { function tryUpload() { uploadFn() .then((results) => resolve(results)) - .catch((e) => { - lastError = e + .catch((error) => { + lastError = error switch (true) { - case e.status >= 400: // observed errors: 408, 401 (4** swallowed), 502 - case e.name === 'FetchError': { + case error.status >= 400: // observed errors: 408, 401 (4** swallowed), 502 + case error.name === 'FetchError': { return fibonacciBackoff.backoff() } default: { - return reject(e) + return reject(error) } } }) diff --git a/src/index.js b/src/index.js index e468027..5ea3dde 100644 --- a/src/index.js +++ b/src/index.js @@ -17,17 +17,15 @@ class NetlifyAPI { accessToken = null } // default opts - opts = Object.assign( - { - userAgent: 'netlify/js-client', - scheme: dfn.schemes[0], - host: dfn.host, - pathPrefix: dfn.basePath, - accessToken, - globalParams: {}, - }, - opts - ) + opts = { + userAgent: 'netlify/js-client', + scheme: dfn.schemes[0], + host: dfn.host, + pathPrefix: dfn.basePath, + accessToken, + globalParams: {}, + ...opts, + } this.defaultHeaders = { 'User-agent': opts.userAgent, @@ -48,7 +46,7 @@ class NetlifyAPI { set accessToken(token) { if (token) { - set(this, 'defaultHeaders.Authorization', 'Bearer ' + token) + set(this, 'defaultHeaders.Authorization', `Bearer ${token}`) } else { delete this.defaultHeaders.Authorization } @@ -59,7 +57,7 @@ class NetlifyAPI { } async getAccessToken(ticket, opts) { - opts = Object.assign({ poll: 1000, timeout: 3.6e6 }, opts) + opts = { poll: 1000, timeout: 3.6e6, ...opts } const api = this @@ -71,7 +69,7 @@ class NetlifyAPI { if (t.authorized) { authorizedTicket = t } - return !!t.authorized + return Boolean(t.authorized) } await pWaitFor(checkTicket, { diff --git a/src/index.test.js b/src/index.test.js index 5936881..86967f1 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ const http = require('http') const test = require('ava') @@ -6,7 +7,7 @@ const { TextHTTPError, JSONHTTPError } = require('micro-api-client') const nock = require('nock') const { v4: uuidv4 } = require('uuid') -const NetlifyAPI = require('./index') +const NetlifyAPI = require('.') const scheme = 'http' const domain = 'localhost' @@ -14,20 +15,25 @@ const port = 1123 const pathPrefix = '/api/v10' const host = `${domain}:${port}` const origin = `${scheme}://${host}` -const accessToken = 'testAccessToken' +const testAccessToken = 'testAccessToken' + +const AGENT_KEEP_ALIVE_MSECS = 60000 +const AGENT_MAX_SOCKETS = 10 +const AGENT_MAX_FREE_SOCKETS = 10 +const AGENT_TIMEOUT = 60000 const agent = new http.Agent({ keepAlive: true, - keepAliveMsecs: 60000, - maxSockets: 10, - maxFreeSockets: 10, - timeout: 60000, + keepAliveMsecs: AGENT_KEEP_ALIVE_MSECS, + maxSockets: AGENT_MAX_SOCKETS, + maxFreeSockets: AGENT_MAX_FREE_SOCKETS, + timeout: AGENT_TIMEOUT, }) const getClient = function (opts = {}) { - return new NetlifyAPI(opts.accessToken, Object.assign({ scheme, host, pathPrefix }, opts)) + return new NetlifyAPI(opts.accessToken, { scheme, host, pathPrefix, ...opts }) } -test('Default options', async (t) => { +test('Default options', (t) => { const client = new NetlifyAPI({}) t.is(client.scheme, 'https') t.is(client.host, 'api.netlify.com') @@ -41,27 +47,27 @@ test('Default options', async (t) => { }) }) -test('Can set|get scheme', async (t) => { +test('Can set|get scheme', (t) => { const client = new NetlifyAPI({ scheme }) t.is(client.scheme, scheme) }) -test('Can set|get host', async (t) => { +test('Can set|get host', (t) => { const client = new NetlifyAPI({ host }) t.is(client.host, host) }) -test('Can set|get pathPrefix', async (t) => { +test('Can set|get pathPrefix', (t) => { const client = new NetlifyAPI({ pathPrefix }) t.is(client.pathPrefix, pathPrefix) }) -test('Can set|get basePath', async (t) => { +test('Can set|get basePath', (t) => { const client = new NetlifyAPI({ scheme, host, pathPrefix }) t.is(client.basePath, `${scheme}://${host}${pathPrefix}`) }) -test('Can update basePath', async (t) => { +test('Can update basePath', (t) => { const client = new NetlifyAPI({ scheme, host, pathPrefix }) const newScheme = 'https' @@ -74,98 +80,98 @@ test('Can update basePath', async (t) => { t.is(client.basePath, `${newScheme}://${newHost}${newPathPrefix}`) }) -test('Can set user agent', async (t) => { +test('Can set user agent', (t) => { const userAgent = 'test' const client = new NetlifyAPI({ userAgent }) t.is(client.defaultHeaders['User-agent'], userAgent) }) -test('Can set|get globalParams', async (t) => { +test('Can set|get globalParams', (t) => { const testGlobalParams = { test: 'test' } const client = new NetlifyAPI({ globalParams: testGlobalParams }) t.deepEqual(client.globalParams, testGlobalParams) }) -test('Can set|get access token', async (t) => { +test('Can set|get access token', (t) => { const client = getClient() - client.accessToken = accessToken - t.is(client.accessToken, accessToken) - t.is(client.defaultHeaders.Authorization, `Bearer ${accessToken}`) + client.accessToken = testAccessToken + t.is(client.accessToken, testAccessToken) + t.is(client.defaultHeaders.Authorization, `Bearer ${testAccessToken}`) client.accessToken = undefined t.is(client.accessToken, null) t.is(client.defaultHeaders.Authorization, undefined) - client.accessToken = accessToken - t.is(client.accessToken, accessToken) - t.is(client.defaultHeaders.Authorization, `Bearer ${accessToken}`) + client.accessToken = testAccessToken + t.is(client.accessToken, testAccessToken) + t.is(client.defaultHeaders.Authorization, `Bearer ${testAccessToken}`) }) -test('Can specify access token as the first argument', async (t) => { - const client = new NetlifyAPI(accessToken, {}) - t.is(client.accessToken, accessToken) +test('Can specify access token as the first argument', (t) => { + const client = new NetlifyAPI(testAccessToken, {}) + t.is(client.accessToken, testAccessToken) }) -test('Can specify access token as an option', async (t) => { - const client = new NetlifyAPI({ accessToken }) - t.is(client.accessToken, accessToken) +test('Can specify access token as an option', (t) => { + const client = new NetlifyAPI({ accessToken: testAccessToken }) + t.is(client.accessToken, testAccessToken) }) test('Can use underscored parameters in path variables', async (t) => { - const account_id = uuidv4() - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200) + const accountId = uuidv4() + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200) const client = getClient() - await client.getAccount({ account_id }) + await client.getAccount({ account_id: accountId }) t.true(scope.isDone()) }) test('Can use camelcase parameters in path variables', async (t) => { - const account_id = uuidv4() - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200) + const accountId = uuidv4() + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200) const client = getClient() - await client.getAccount({ accountId: account_id }) + await client.getAccount({ accountId }) t.true(scope.isDone()) }) test('Can use global parameters in path variables', async (t) => { - const account_id = uuidv4() - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200) + const accountId = uuidv4() + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200) - const client = getClient({ globalParams: { account_id } }) + const client = getClient({ globalParams: { account_id: accountId } }) await client.getAccount() t.true(scope.isDone()) }) test('Can use underscored parameters in query variables', async (t) => { - const client_id = uuidv4() - const scope = nock(origin).post(`${pathPrefix}/oauth/tickets`).query({ client_id }).reply(200) + const clientId = uuidv4() + const scope = nock(origin).post(`${pathPrefix}/oauth/tickets`).query({ client_id: clientId }).reply(200) const client = getClient() - await client.createTicket({ client_id }) + await client.createTicket({ client_id: clientId }) t.true(scope.isDone()) }) test('Can use camelcase parameters in query variables', async (t) => { - const client_id = uuidv4() - const scope = nock(origin).post(`${pathPrefix}/oauth/tickets`).query({ client_id }).reply(200) + const clientId = uuidv4() + const scope = nock(origin).post(`${pathPrefix}/oauth/tickets`).query({ client_id: clientId }).reply(200) const client = getClient() - await client.createTicket({ clientId: client_id }) + await client.createTicket({ clientId }) t.true(scope.isDone()) }) test('Can use global parameters in query variables', async (t) => { - const client_id = uuidv4() - const scope = nock(origin).post(`${pathPrefix}/oauth/tickets`).query({ client_id }).reply(200) + const clientId = uuidv4() + const scope = nock(origin).post(`${pathPrefix}/oauth/tickets`).query({ client_id: clientId }).reply(200) - const client = getClient({ globalParams: { client_id } }) + const client = getClient({ globalParams: { client_id: clientId } }) await client.createTicket() t.true(scope.isDone()) @@ -192,32 +198,32 @@ test('Can specify JSON request body as a function', async (t) => { }) test('Can specify binary request body as a stream', async (t) => { - const deploy_id = uuidv4() + const deployId = uuidv4() const path = 'testPath' const body = 'test' const expectedResponse = { test: 'test' } const scope = nock(origin) - .put(`${pathPrefix}/deploys/${deploy_id}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) + .put(`${pathPrefix}/deploys/${deployId}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) .reply(200, expectedResponse) const client = getClient() - const response = await client.uploadDeployFile({ deploy_id, path, body: fromString(body) }) + const response = await client.uploadDeployFile({ deploy_id: deployId, path, body: fromString(body) }) t.deepEqual(response, expectedResponse) t.true(scope.isDone()) }) test('Can specify binary request body as a function', async (t) => { - const deploy_id = uuidv4() + const deployId = uuidv4() const path = 'testPath' const body = 'test' const expectedResponse = { test: 'test' } const scope = nock(origin) - .put(`${pathPrefix}/deploys/${deploy_id}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) + .put(`${pathPrefix}/deploys/${deployId}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) .reply(200, expectedResponse) const client = getClient() - const response = await client.uploadDeployFile({ deploy_id, path, body: () => fromString(body) }) + const response = await client.uploadDeployFile({ deploy_id: deployId, path, body: () => fromString(body) }) t.deepEqual(response, expectedResponse) t.true(scope.isDone()) @@ -234,8 +240,8 @@ test('Can use global parameters in request body', async (t) => { }) test('Validates required path parameters', async (t) => { - const account_id = uuidv4() - const scope = nock(origin).put(`${pathPrefix}/accounts/${account_id}`).reply(200) + const accountId = uuidv4() + const scope = nock(origin).put(`${pathPrefix}/accounts/${accountId}`).reply(200) const client = getClient() await t.throwsAsync(client.updateAccount(), "Missing required path variable 'account_id'") @@ -244,11 +250,14 @@ test('Validates required path parameters', async (t) => { }) test('Validates required query parameters', async (t) => { - const account_slug = uuidv4() - const scope = nock(origin).post(`${pathPrefix}/${account_slug}/members`).reply(200) + const accountSlug = uuidv4() + const scope = nock(origin).post(`${pathPrefix}/${accountSlug}/members`).reply(200) const client = getClient() - await t.throwsAsync(client.addMemberToAccount({ account_slug }), "Missing required query variable 'email'") + await t.throwsAsync( + client.addMemberToAccount({ account_slug: accountSlug }), + "Missing required query variable 'email'", + ) t.false(scope.isDone()) }) @@ -256,46 +265,46 @@ test('Validates required query parameters', async (t) => { test('Can set request headers', async (t) => { const headerName = 'test' const headerValue = 'test' - const account_id = uuidv4() - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).matchHeader(headerName, headerValue).reply(200) + const accountId = uuidv4() + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).matchHeader(headerName, headerValue).reply(200) const client = getClient() - await client.getAccount({ account_id }, { headers: { [headerName]: headerValue } }) + await client.getAccount({ account_id: accountId }, { headers: { [headerName]: headerValue } }) t.true(scope.isDone()) }) test('Can parse JSON responses', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const expectedResponse = { test: 'test' } - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200, expectedResponse) + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200, expectedResponse) const client = getClient() - const response = await client.getAccount({ account_id }) + const response = await client.getAccount({ account_id: accountId }) t.deepEqual(response, expectedResponse) t.true(scope.isDone()) }) test('Can parse text responses', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const expectedResponse = 'test' - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200, expectedResponse) + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200, expectedResponse) const client = getClient() - const response = await client.getAccount({ account_id }) + const response = await client.getAccount({ account_id: accountId }) t.deepEqual(response, expectedResponse) t.true(scope.isDone()) }) test('Handle error empty responses', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const status = 404 - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(status) + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(status) const client = getClient() - const error = await t.throwsAsync(client.getAccount({ account_id })) + const error = await t.throwsAsync(client.getAccount({ account_id: accountId })) t.is(error.status, status) t.is(error.message, 'Not Found') @@ -306,13 +315,13 @@ test('Handle error empty responses', async (t) => { }) test('Handle error text responses', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const status = 404 const expectedResponse = 'test' - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(status, expectedResponse) + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(status, expectedResponse) const client = getClient() - const error = await t.throwsAsync(client.getAccount({ account_id })) + const error = await t.throwsAsync(client.getAccount({ account_id: accountId })) t.is(error.status, status) t.is(error.message, 'Not Found') @@ -323,15 +332,15 @@ test('Handle error text responses', async (t) => { }) test('Handle error text responses on JSON endpoints', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const status = 404 const expectedResponse = 'test' const scope = nock(origin) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(status, expectedResponse, { 'Content-Type': 'application/json' }) const client = getClient() - const error = await t.throwsAsync(client.getAccount({ account_id })) + const error = await t.throwsAsync(client.getAccount({ account_id: accountId })) t.is(error.status, status) t.is(error.message, 'Not Found') @@ -342,13 +351,13 @@ test('Handle error text responses on JSON endpoints', async (t) => { }) test('Handle error JSON responses', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const status = 404 const errorJson = { error: true } - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(status, errorJson) + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(status, errorJson) const client = getClient() - const error = await t.throwsAsync(client.getAccount({ account_id })) + const error = await t.throwsAsync(client.getAccount({ account_id: accountId })) t.is(error.status, status) t.is(error.message, 'Not Found') @@ -359,13 +368,13 @@ test('Handle error JSON responses', async (t) => { }) test('Handle network errors', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const expectedResponse = 'test' - const url = `${pathPrefix}/accounts/${account_id}` + const url = `${pathPrefix}/accounts/${accountId}` const scope = nock(origin).get(url).replyWithError(expectedResponse) const client = getClient() - const error = await t.throwsAsync(client.getAccount({ account_id })) + const error = await t.throwsAsync(client.getAccount({ account_id: accountId })) t.true(error instanceof Error) t.is(error.name, 'FetchError') @@ -376,89 +385,90 @@ test('Handle network errors', async (t) => { }) test('Can get an access token from a ticket', async (t) => { - const ticket_id = uuidv4() - const access_token = 'test' + const ticketId = uuidv4() + const accessToken = 'test' const scope = nock(origin) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) - .reply(200, { authorized: true, id: ticket_id }) - .post(`${pathPrefix}/oauth/tickets/${ticket_id}/exchange`) - .reply(200, { access_token }) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) + .reply(200, { authorized: true, id: ticketId }) + .post(`${pathPrefix}/oauth/tickets/${ticketId}/exchange`) + .reply(200, { access_token: accessToken }) const client = getClient() - const response = await client.getAccessToken({ id: ticket_id, poll: 1, timeout: 5e3 }) + const timeout = 5e3 + const response = await client.getAccessToken({ id: ticketId, poll: 1, timeout }) - t.is(response, access_token) - t.is(client.accessToken, access_token) + t.is(response, accessToken) + t.is(client.accessToken, accessToken) t.true(scope.isDone()) }) test('Can poll for access token', async (t) => { - const ticket_id = uuidv4() - const access_token = 'test' + const ticketId = uuidv4() + const accessToken = 'test' const scope = nock(origin) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) .reply(200, {}) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) - .reply(200, { authorized: true, id: ticket_id }) - .post(`${pathPrefix}/oauth/tickets/${ticket_id}/exchange`) - .reply(200, { access_token }) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) + .reply(200, { authorized: true, id: ticketId }) + .post(`${pathPrefix}/oauth/tickets/${ticketId}/exchange`) + .reply(200, { access_token: accessToken }) const client = getClient() - await client.getAccessToken({ id: ticket_id }) + await client.getAccessToken({ id: ticketId }) t.true(scope.isDone()) }) test('Can change access token polling', async (t) => { - const ticket_id = uuidv4() - const access_token = 'test' + const ticketId = uuidv4() + const accessToken = 'test' const scope = nock(origin) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) .reply(200, {}) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) - .reply(200, { authorized: true, id: ticket_id }) - .post(`${pathPrefix}/oauth/tickets/${ticket_id}/exchange`) - .reply(200, { access_token }) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) + .reply(200, { authorized: true, id: ticketId }) + .post(`${pathPrefix}/oauth/tickets/${ticketId}/exchange`) + .reply(200, { access_token: accessToken }) const client = getClient() - await client.getAccessToken({ id: ticket_id }, { poll: 1 }) + await client.getAccessToken({ id: ticketId }, { poll: 1 }) t.true(scope.isDone()) }) test('Can timeout access token polling', async (t) => { - const ticket_id = uuidv4() - const access_token = 'test' + const ticketId = uuidv4() + const accessToken = 'test' const scope = nock(origin) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) .reply(200, {}) - .get(`${pathPrefix}/oauth/tickets/${ticket_id}`) - .reply(200, { authorized: true, id: ticket_id }) - .post(`${pathPrefix}/oauth/tickets/${ticket_id}/exchange`) - .reply(200, { access_token }) + .get(`${pathPrefix}/oauth/tickets/${ticketId}`) + .reply(200, { authorized: true, id: ticketId }) + .post(`${pathPrefix}/oauth/tickets/${ticketId}/exchange`) + .reply(200, { access_token: accessToken }) const client = getClient() await t.throwsAsync( - client.getAccessToken({ id: ticket_id }, { poll: 1, timeout: 1 }), - 'Promise timed out after 1 milliseconds' + client.getAccessToken({ id: ticketId }, { poll: 1, timeout: 1 }), + 'Promise timed out after 1 milliseconds', ) t.false(scope.isDone()) }) test('Handles API rate limiting', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const retryAtMs = Date.now() + TEST_RATE_LIMIT_DELAY const retryAt = Math.ceil(retryAtMs / SECS_TO_MSECS) const expectedResponse = { test: 'test' } const scope = nock(origin) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(429, { retryAt }, { 'X-RateLimit-Reset': retryAt }) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(200, expectedResponse) const client = getClient() - const response = await client.getAccount({ account_id }) + const response = await client.getAccount({ account_id: accountId }) t.true(Date.now() >= retryAtMs) t.deepEqual(response, expectedResponse) @@ -466,49 +476,50 @@ test('Handles API rate limiting', async (t) => { }) test('Handles API rate limiting when date is in the past', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const expectedResponse = { test: 'test' } const scope = nock(origin) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(429, { retryAt: 0 }, { 'X-RateLimit-Reset': 0 }) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(200, expectedResponse) const client = getClient() - await client.getAccount({ account_id }) + await client.getAccount({ account_id: accountId }) t.true(scope.isDone()) }) test('Handles API rate limiting when X-RateLimit-Reset is missing', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const expectedResponse = { test: 'test' } const retryAt = 'invalid' const scope = nock(origin) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(429, { retryAt }) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(200, expectedResponse) const client = getClient() - await client.getAccount({ account_id }) + await client.getAccount({ account_id: accountId }) t.true(scope.isDone()) }) test('Gives up retrying on API rate limiting after a timeout', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const retryAt = Math.ceil(Date.now() / SECS_TO_MSECS) const expectedResponse = { test: 'test' } + const times = 20 const scope = nock(origin) - .get(`${pathPrefix}/accounts/${account_id}`) - .times(20) + .get(`${pathPrefix}/accounts/${accountId}`) + .times(times) .reply(429, { retryAt }, { 'X-RateLimit-Reset': retryAt }) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(200, expectedResponse) const client = getClient() - const error = await t.throwsAsync(client.getAccount({ account_id })) + const error = await t.throwsAsync(client.getAccount({ account_id: accountId })) t.is(error.status, 429) t.is(error.message, 'Too Many Requests') @@ -518,17 +529,17 @@ test('Gives up retrying on API rate limiting after a timeout', async (t) => { }) test('Retries on ETIMEDOUT connection errors', async (t) => { - const account_id = uuidv4() + const accountId = uuidv4() const retryAtMs = Date.now() + TEST_RATE_LIMIT_DELAY const expectedResponse = { test: 'test' } const scope = nock(origin) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .replyWithError({ code: 'ETIMEDOUT' }) - .get(`${pathPrefix}/accounts/${account_id}`) + .get(`${pathPrefix}/accounts/${accountId}`) .reply(200, expectedResponse) const client = getClient() - const response = await client.getAccount({ account_id }) + const response = await client.getAccount({ account_id: accountId }) t.true(Date.now() >= retryAtMs) t.deepEqual(response, expectedResponse) @@ -536,47 +547,48 @@ test('Retries on ETIMEDOUT connection errors', async (t) => { }) test('Recreates a function body when handling API rate limiting', async (t) => { - const deploy_id = uuidv4() + const deployId = uuidv4() const path = 'testPath' const body = 'test' const retryAtMs = Date.now() + TEST_RATE_LIMIT_DELAY const retryAt = Math.ceil(retryAtMs / SECS_TO_MSECS) const expectedResponse = { test: 'test' } const scope = nock(origin) - .put(`${pathPrefix}/deploys/${deploy_id}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) + .put(`${pathPrefix}/deploys/${deployId}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) .reply(429, { retryAt }, { 'X-RateLimit-Reset': retryAt }) - .put(`${pathPrefix}/deploys/${deploy_id}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) + .put(`${pathPrefix}/deploys/${deployId}/files/${path}`, body, { 'Content-Type': 'application/octet-stream' }) .reply(200, expectedResponse) const client = getClient() - const response = await client.uploadDeployFile({ deploy_id, path, body: () => fromString(body) }) + const response = await client.uploadDeployFile({ deploy_id: deployId, path, body: () => fromString(body) }) t.true(Date.now() >= retryAtMs) t.deepEqual(response, expectedResponse) t.true(scope.isDone()) }) -test('Can set (proxy) agent', async (t) => { - const client = getClient({ accessToken, agent }) +test('Can set (proxy) agent', (t) => { + const client = getClient({ accessToken: testAccessToken, agent }) t.is(client.agent, agent) }) test('(Proxy) agent is passed as request option', async (t) => { - const account_id = uuidv4() - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200) + const accountId = uuidv4() + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200) - const client = getClient({ accessToken, agent }) - await client.getAccount({ account_id }) + const client = getClient({ accessToken: testAccessToken, agent }) + await client.getAccount({ account_id: accountId }) t.is(scope.interceptors[0].req.options.agent, agent) }) test('(Proxy) agent is not passed as request option if not set', async (t) => { - const account_id = uuidv4() - const scope = nock(origin).get(`${pathPrefix}/accounts/${account_id}`).reply(200) + const accountId = uuidv4() + const scope = nock(origin).get(`${pathPrefix}/accounts/${accountId}`).reply(200) - const client = getClient({ accessToken }) - await client.getAccount({ account_id }) + const client = getClient({ accessToken: testAccessToken }) + await client.getAccount({ account_id: accountId }) t.falsy(scope.interceptors[0].req.options.agent) }) const TEST_RATE_LIMIT_DELAY = 5e3 const SECS_TO_MSECS = 1e3 +/* eslint-enable max-lines */ diff --git a/src/methods/index.js b/src/methods/index.js index 4f46337..464a62e 100644 --- a/src/methods/index.js +++ b/src/methods/index.js @@ -29,7 +29,7 @@ const getMethod = function (method, NetlifyApi) { } const callMethod = async function (method, NetlifyApi, params, opts) { - const requestParams = Object.assign({}, NetlifyApi.globalParams, params) + const requestParams = { ...NetlifyApi.globalParams, ...params } const url = getUrl(method, NetlifyApi, requestParams) const response = await makeRequestOrRetry({ url, method, NetlifyApi, requestParams, opts }) @@ -47,23 +47,20 @@ const getOpts = function ({ verb, parameters }, NetlifyApi, { body }, opts) { // Add the HTTP method based on the OpenAPI definition const addHttpMethod = function (verb, opts) { - return Object.assign({}, opts, { method: verb.toUpperCase() }) + return { ...opts, method: verb.toUpperCase() } } // Assign default HTTP headers const addDefaultHeaders = function (NetlifyApi, opts) { - return Object.assign({}, opts, { - headers: Object.assign({}, NetlifyApi.defaultHeaders, opts.headers), - }) + return { ...opts, headers: { ...NetlifyApi.defaultHeaders, ...opts.headers } } } // Assign fetch agent (like for example HttpsProxyAgent) if there is one const addAgent = function (NetlifyApi, opts) { if (NetlifyApi.agent) { - return Object.assign({}, opts, { agent: NetlifyApi.agent }) - } else { - return opts + return { ...opts, agent: NetlifyApi.agent } } + return opts } const makeRequestOrRetry = async function ({ url, method, NetlifyApi, requestParams, opts }) { diff --git a/src/methods/response.js b/src/methods/response.js index f4431fe..b807ba3 100644 --- a/src/methods/response.js +++ b/src/methods/response.js @@ -38,7 +38,7 @@ const parseJsonResponse = function (response, textResponse, responseType) { } const getFetchError = function (error, url, opts) { - const data = Object.assign({}, opts) + const data = { ...opts } delete data.Authorization Object.assign(error, { name: 'FetchError', url, data }) return error diff --git a/src/methods/retry.js b/src/methods/retry.js index 0f2f6c7..b09db69 100644 --- a/src/methods/retry.js +++ b/src/methods/retry.js @@ -2,7 +2,7 @@ // - when receiving a rate limiting response // - on network failures due to timeouts const shouldRetry = function ({ response = {}, error = {} }) { - return response.status === RATE_LIMIT_STATUS || RETRY_ERROR_CODES.includes(error.code) + return response.status === RATE_LIMIT_STATUS || RETRY_ERROR_CODES.has(error.code) } const waitForRetry = async function (response) { @@ -34,6 +34,6 @@ const SECS_TO_MSECS = 1e3 const MAX_RETRY = 10 const RATE_LIMIT_STATUS = 429 const RATE_LIMIT_HEADER = 'X-RateLimit-Reset' -const RETRY_ERROR_CODES = ['ETIMEDOUT'] +const RETRY_ERROR_CODES = new Set(['ETIMEDOUT']) module.exports = { shouldRetry, waitForRetry, MAX_RETRY } diff --git a/src/operations.js b/src/operations.js index 616850c..cd9a22b 100644 --- a/src/operations.js +++ b/src/operations.js @@ -9,9 +9,9 @@ const getOperations = function () { const operations = omit(pathItem, ['parameters']) return Object.entries(operations).map(([method, operation]) => { const parameters = getParameters(pathItem.parameters, operation.parameters) - return Object.assign({}, operation, { verb: method, path, parameters }) + return { ...operation, verb: method, path, parameters } }) - }) + }), ) } @@ -21,9 +21,7 @@ const getParameters = function (pathParameters = [], operationParameters = []) { } const addParameter = function (parameters, param) { - return Object.assign({}, parameters, { - [param.in]: Object.assign({}, parameters[param.in], { [param.name]: param }), - }) + return { ...parameters, [param.in]: { ...parameters[param.in], [param.name]: param } } } module.exports = { getOperations }