diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..79f18a28 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +Dockerfile +CHANGELOG.md +Procfile +README.md +docs +http +jest.config.js +.eslintrc.js +.github +.gitignore +.prettierrc +Dockerrun.aws.json.tpl +Dockerrun.aws.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..0fbd99b7 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + 'prettier/@typescript-eslint', + ], + root: true, + env: { + node: true, + jest: true, + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}; diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..3095a367 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: ":arrow_up: " + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: ":arrow_up: " diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..21890acf --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,12 @@ +config: + - ./* +tooling: + - tooling/**/*.* +assets: + - static/**/*.* +tests: + - any: ["src/**/*.spec.js", "cypress/**/*"] +package: + - any: ["package.json", "package-lock.json"] +source: + - src/**/* diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000..3e657b34 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,35 @@ +name: Merge PRs +on: + pull_request: + types: + - labeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: Automerge + uses: pascalgn/automerge-action@v0.13.1 + env: + GITHUB_TOKEN: "${{ secrets.GH_PAT }}" + MERGE_LABELS: "merge,!work in progress,!wip" + MERGE_REMOVE_LABELS: "merge" + MERGE_METHOD: "merge" + MERGE_COMMIT_MESSAGE: ":twisted_rightwards_arrows: Merge #{pullRequest.number} ({pullRequest.title})" + MERGE_FORKS: false + UPDATE_LABELS: "merge" + UPDATE_METHOD: "merge" + - name: Delete merged branch + uses: koj-co/delete-merged-action@master + with: + branches: "!master, !production, *" + env: + GITHUB_TOKEN: "${{ secrets.GH_PAT }}" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..93da6457 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,35 @@ +name: CodeQL CI +on: + schedule: + - cron: '0 0 * * 1' +jobs: + release: + name: Build and analyze + runs-on: ubuntu-18.04 + if: "!contains(github.event.head_commit.message, '[skip ci]')" + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Setup Node.js + uses: actions/setup-node@v2.1.5 + with: + node-version: 14 + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: javascript + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Install dependencies + run: npm ci + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 00000000..ecb1772b --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,15 @@ +name: Dependabot PR CI +on: + schedule: + - cron: "0 */6 * * *" + workflow_dispatch: +jobs: + label-approve: + name: Label and approve minor/patch updates + runs-on: ubuntu-18.04 + steps: + - uses: koj-co/dependabot-pr-action@master + with: + token: ${{ secrets.GH_PAT }} + merge-minor: true + merge-patch: true diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..8d10a0ea --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,18 @@ +name: Pull Request Labeler +on: + - pull_request + - pull_request_review +jobs: + triage: + runs-on: ubuntu-latest + steps: + - name: Label all PRs + uses: actions/labeler@master + with: + repo-token: "${{ secrets.GH_PAT }}" + - name: Label approved PRs + uses: koj-co/label-approved-action@master + with: + labels: "merge" + env: + GITHUB_TOKEN: "${{ secrets.GH_PAT }}" diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml new file mode 100644 index 00000000..c884411e --- /dev/null +++ b/.github/workflows/node.yml @@ -0,0 +1,82 @@ +name: Node CI +on: + push: + branches: + - master +jobs: + test: + name: Build, test, and release + if: "!contains(github.event.head_commit.message, '[skip ci]')" + strategy: + matrix: + os: [ubuntu-18.04, ubuntu-20.04] + node: [14] + runs-on: ${{ matrix.os }} + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: api-test + ports: + - 3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Setup Node.js + uses: actions/setup-node@v2.1.5 + with: + node-version: ${{ matrix.node }} + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ matrix.os }}-${{ matrix.node }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.node }}-${{ env.cache-name }}- + ${{ matrix.os }}-${{ matrix.node }}- + ${{ matrix.os }}- + - name: Install dependencies + run: npm ci + - name: Build TypeScript + run: npm run build + - name: Run tests + run: npm run test + env: + DB_PORT: ${{ job.services.mysql.ports['3306'] }} + release: + name: Release + runs-on: ubuntu-20.04 + needs: [test] + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Setup Node.js + uses: actions/setup-node@v2.1.5 + with: + node-version: ${{ matrix.node }} + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ matrix.os }}-${{ matrix.node }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.node }}-${{ env.cache-name }}- + ${{ matrix.os }}-${{ matrix.node }}- + ${{ matrix.os }}- + - name: Install dependencies + run: npm ci + - name: Release + run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GIT_AUTHOR_NAME: 'FindingAnand' + GIT_AUTHOR_EMAIL: 'bot@anandchowdhary.com' + GIT_COMMITTER_NAME: 'FindingAnand' + GIT_COMMITTER_EMAIL: 'bot@anandchowdhary.com' diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 00000000..b57c7ade --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,73 @@ +name: PR Generator CI +on: + push: + branches-ignore: + - master + - production +jobs: + auto-pull-request: + name: PullRequestAction + runs-on: ubuntu-latest + steps: + - name: Generate branch name + uses: actions/github-script@v3 + id: set-branch-name + with: + script: | + const capitalize = (name) => name.charAt(0).toUpperCase() + name.slice(1); + const emoji = context.payload.ref.startsWith("refs/heads/feature") + ? "✨ " + : context.payload.ref.startsWith("refs/heads/hotfix") + ? "🚑 " + : context.payload.ref.startsWith("refs/heads/bug") + ? "🐛 " + : ""; + return `${emoji}${capitalize( + context.payload.ref + .replace("refs/heads/", "") + .replace(/-/g, " ") + .replace("feature ", "") + .replace("bug ", "") + .replace("hotfix ", "") + )}`; + result-encoding: string + - name: Set branch name + run: echo "PULL_REQUEST_TITLE=${{steps.set-branch-name.outputs.result}}" >> $GITHUB_ENV + - name: Generate PR body + uses: actions/github-script@v3 + id: set-pr-body + with: + script: | + return `I'm opening this pull request for this branch, pushed by @${ + context.payload.head_commit.author.username + } with ${context.payload.commits.length} commit${ + context.payload.commits.length === 1 ? "" : "s" + }.`; + result-encoding: string + - name: Set PR body + run: echo "PULL_REQUEST_BODY=${{steps.set-pr-body.outputs.result}}" >> $GITHUB_ENV + - name: Generate PR draft + uses: actions/github-script@v3 + id: set-pr-draft + with: + script: | + return !context.payload.ref.startsWith("refs/heads/hotfix"); + - name: Set PR draft + run: echo "PULL_REQUEST_DRAFT=${{steps.set-pr-draft.outputs.result}}" >> $GITHUB_ENV + - name: Determine whether to merge + uses: actions/github-script@v3 + id: should-pr + with: + github-token: ${{ secrets.GH_PAT }} + script: | + return + context.payload.ref.startsWith("refs/heads/feature") || + context.payload.ref.startsWith("refs/heads/hotfix") || + context.payload.ref.startsWith("refs/heads/bug"); + - name: pull-request-action + uses: vsoch/pull-request-action@1.0.15 + if: always() && steps.should-pr.outputs.result + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + PULL_REQUEST_BRANCH: "master" + PULL_REQUEST_REVIEWERS: "AnandChowdhary" diff --git a/.github/workflows/release-scheduler.yml b/.github/workflows/release-scheduler.yml new file mode 100644 index 00000000..466e0219 --- /dev/null +++ b/.github/workflows/release-scheduler.yml @@ -0,0 +1,13 @@ +name: Release Scheduler CI +on: + schedule: + - cron: "0 0 * * 1" + workflow_dispatch: +jobs: + releaseScheduler: + runs-on: ubuntu-latest + steps: + - name: Run release-scheduler + uses: koj-co/release-scheduler@master + env: + GH_PAT: ${{ secrets.GH_PAT }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..dcd6e8b9 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: "Stale Issues CI" +on: + schedule: + - cron: "0 0 * * *" +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GH_PAT }} + stale-issue-message: "⚠️ This issue has not seen any activity in the past 2 months so I'm marking it as stale. I'll close it if it doesn't see any activity in the coming week." + stale-pr-message: "⚠️ This PR has not seen any activity in the past 2 months so I'm marking it as stale. I'll close it if it doesn't see any activity in the coming week." + days-before-stale: 60 + days-before-close: 7 + stale-issue-label: "wontfix" + exempt-issue-labels: "wip" + stale-pr-label: "wontfix" + exempt-pr-labels: "wip" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..caa966e8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,49 @@ +name: Test CI +on: + push: + branches-ignore: + - master +jobs: + release: + name: Build and test + if: "!contains(github.event.head_commit.message, '[skip ci]')" + strategy: + matrix: + os: [ubuntu-18.04, ubuntu-20.04] + node: [14] + runs-on: ${{ matrix.os }} + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: api-test + ports: + - 3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Setup Node.js + uses: actions/setup-node@v2.1.5 + with: + node-version: ${{ matrix.node }} + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ matrix.os }}-${{ matrix.node }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.node }}-${{ env.cache-name }}- + ${{ matrix.os }}-${{ matrix.node }}- + ${{ matrix.os }}- + - name: Install dependencies + run: npm ci + - name: Build TypeScript + run: npm run build + - name: Run tests + run: npm run test + env: + DB_PORT: ${{ job.services.mysql.ports['3306'] }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9def7e12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Dotenv +.env diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..e0d3dd55 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +.vscode +.licenses +.github diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..dcb72794 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/.templaterc.json b/.templaterc.json new file mode 100644 index 00000000..acb0c2cc --- /dev/null +++ b/.templaterc.json @@ -0,0 +1,10 @@ +{ + "files": [ + "*", + ".github/**/*", + "prisma/**/*", + "src/**/*", + "static/**/*", + "test/**/*" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..88574dba --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,845 @@ +## v4.0.9 (2021-03-15) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.9) · [💻 Compare](https://github.com/staart/api/compare/v4.0.8...v4.0.9) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.9) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.9.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.9.tar.gz)) + +### ⬆️ Dependency updates + +- [`6982a103`](https://github.com/staart/api/commit/6982a103) Bump @types/node from 14.14.30 to 14.14.32 + +## v4.0.8 (2021-03-01) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.8) · [💻 Compare](https://github.com/staart/api/compare/v4.0.7...v4.0.8) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.8) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.8.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.8.tar.gz)) + +### ⬆️ Dependency updates + +- [`e60fd38e`](https://github.com/staart/api/commit/e60fd38e) Bump @nestjs/core from 7.6.11 to 7.6.13 +- [`2ce3004a`](https://github.com/staart/api/commit/2ce3004a) Bump aws-sdk from 2.846.0 to 2.849.0 +- [`8c411507`](https://github.com/staart/api/commit/8c411507) Bump actions/setup-node from v2.1.4 to v2.1.5 +- [`dbb3b74c`](https://github.com/staart/api/commit/dbb3b74c) Bump p-retry from 4.3.0 to 4.4.0 +- [`f64a5110`](https://github.com/staart/api/commit/f64a5110) Bump @nestjs/cli from 7.5.4 to 7.5.5 +- [`bcae7edd`](https://github.com/staart/api/commit/bcae7edd) Bump @typescript-eslint/parser from 4.15.0 to 4.15.2 +- [`691fd32f`](https://github.com/staart/api/commit/691fd32f) Bump aws-sdk from 2.849.0 to 2.850.0 + +## v4.0.7 (2021-02-22) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.7) · [💻 Compare](https://github.com/staart/api/compare/v4.0.6...v4.0.7) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.7) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.7.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.7.tar.gz)) + +### ⬆️ Dependency updates + +- [`ae120229`](https://github.com/staart/api/commit/ae120229) Bump @typescript-eslint/eslint-plugin from 4.14.2 to 4.15.0 +- [`866477fa`](https://github.com/staart/api/commit/866477fa) Bump vsoch/pull-request-action from 1.0.13 to 1.0.14 +- [`6611b210`](https://github.com/staart/api/commit/6611b210) Bump pascalgn/automerge-action from v0.13.0 to v0.13.1 +- [`5e9246cb`](https://github.com/staart/api/commit/5e9246cb) Bump @types/puppeteer from 5.4.2 to 5.4.3 +- [`11795072`](https://github.com/staart/api/commit/11795072) Bump @types/node from 14.14.25 to 14.14.28 +- [`fd3d507d`](https://github.com/staart/api/commit/fd3d507d) Bump @octokit/rest from 18.1.0 to 18.1.1 +- [`b04fc4ec`](https://github.com/staart/api/commit/b04fc4ec) Bump typescript from 4.1.3 to 4.1.5 +- [`f3455510`](https://github.com/staart/api/commit/f3455510) Bump vsoch/pull-request-action from 1.0.14 to 1.0.15 +- [`0330ddd0`](https://github.com/staart/api/commit/0330ddd0) Bump @nestjs/swagger from 4.7.12 to 4.7.13 +- [`370a2194`](https://github.com/staart/api/commit/370a2194) Bump @nestjs/testing from 7.6.11 to 7.6.12 +- [`d3346c2a`](https://github.com/staart/api/commit/d3346c2a) Bump @elastic/elasticsearch from 7.10.0 to 7.11.0 +- [`04d3337d`](https://github.com/staart/api/commit/04d3337d) Bump ua-parser-js from 0.7.23 to 0.7.24 +- [`c0b527de`](https://github.com/staart/api/commit/c0b527de) Bump aws-sdk from 2.834.0 to 2.845.0 +- [`a4418322`](https://github.com/staart/api/commit/a4418322) Bump @nestjs/platform-express from 7.6.11 to 7.6.12 +- [`48172884`](https://github.com/staart/api/commit/48172884) Bump aws-sdk from 2.845.0 to 2.846.0 +- [`087fc2af`](https://github.com/staart/api/commit/087fc2af) Bump crypto-random-string from 3.3.0 to 3.3.1 +- [`fa66722d`](https://github.com/staart/api/commit/fa66722d) Bump @types/fs-extra from 9.0.6 to 9.0.7 +- [`7567b09c`](https://github.com/staart/api/commit/7567b09c) Bump @types/cheerio from 0.22.23 to 0.22.24 +- [`7e95a6ab`](https://github.com/staart/api/commit/7e95a6ab) Bump @types/node from 14.14.28 to 14.14.30 +- [`2a6f4ea0`](https://github.com/staart/api/commit/2a6f4ea0) Bump twilio from 3.55.1 to 3.56.0 +- [`a014d424`](https://github.com/staart/api/commit/a014d424) Bump nunjucks from 3.2.2 to 3.2.3 +- [`7218f2ff`](https://github.com/staart/api/commit/7218f2ff) Bump stripe from 8.135.0 to 8.137.0 +- [`18d64b74`](https://github.com/staart/api/commit/18d64b74) Bump nodemailer from 6.4.17 to 6.4.18 +- [`f5009cb7`](https://github.com/staart/api/commit/f5009cb7) Bump @octokit/rest from 18.1.1 to 18.2.0 +- [`39d546b0`](https://github.com/staart/api/commit/39d546b0) Bump eslint from 7.19.0 to 7.20.0 + +## v4.0.6 (2021-02-15) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.6) · [💻 Compare](https://github.com/staart/api/compare/v4.0.5...v4.0.6) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.6) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.6.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.6.tar.gz)) + +### ⬆️ Dependency updates + +- [`1a7cad3d`](https://github.com/staart/api/commit/1a7cad3d) Bump stripe from 8.132.0 to 8.134.0 +- [`be45b563`](https://github.com/staart/api/commit/be45b563) Bump @types/node from 14.14.22 to 14.14.25 +- [`74fe75ab`](https://github.com/staart/api/commit/74fe75ab) Bump @nestjs/core from 7.6.8 to 7.6.11 +- [`ae3a30c3`](https://github.com/staart/api/commit/ae3a30c3) Bump @types/nunjucks from 3.1.3 to 3.1.4 +- [`ab248292`](https://github.com/staart/api/commit/ab248292) Bump @sentry/node from 6.0.4 to 6.1.0 +- [`e84a503c`](https://github.com/staart/api/commit/e84a503c) Bump stripe from 8.134.0 to 8.135.0 +- [`5e6ac37e`](https://github.com/staart/api/commit/5e6ac37e) Bump ts-loader from 8.0.14 to 8.0.16 +- [`479707c4`](https://github.com/staart/api/commit/479707c4) Bump @typescript-eslint/parser from 4.14.2 to 4.15.0 + +## v4.0.5 (2021-02-08) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.5) · [💻 Compare](https://github.com/staart/api/compare/v4.0.4...v4.0.5) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.5) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.5.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.5.tar.gz)) + +### ⬆️ Dependency updates + +- [`e7ab1d35`](https://github.com/staart/api/commit/e7ab1d35) Bump @sentry/node from 6.0.2 to 6.0.3 +- [`e59eea6a`](https://github.com/staart/api/commit/e59eea6a) Bump cloudinary from 1.23.0 to 1.24.0 +- [`6d3a3fd5`](https://github.com/staart/api/commit/6d3a3fd5) Bump eslint from 7.18.0 to 7.19.0 +- [`e35e5c18`](https://github.com/staart/api/commit/e35e5c18) Bump ts-jest from 26.4.4 to 26.5.0 +- [`0cb227e4`](https://github.com/staart/api/commit/0cb227e4) Bump @octokit/rest from 18.0.14 to 18.0.15 +- [`a55f5155`](https://github.com/staart/api/commit/a55f5155) Bump @nestjs/testing from 7.6.7 to 7.6.8 +- [`a4853571`](https://github.com/staart/api/commit/a4853571) Bump @sentry/node from 6.0.3 to 6.0.4 +- [`937d45a5`](https://github.com/staart/api/commit/937d45a5) Bump @nestjs/core from 7.6.7 to 7.6.8 +- [`c70193f3`](https://github.com/staart/api/commit/c70193f3) Bump @typescript-eslint/parser from 4.14.1 to 4.14.2 +- [`390c9eb7`](https://github.com/staart/api/commit/390c9eb7) Bump @nestjs/platform-express from 7.6.7 to 7.6.8 +- [`954548c3`](https://github.com/staart/api/commit/954548c3) Bump aws-sdk from 2.833.0 to 2.834.0 +- [`342eb12b`](https://github.com/staart/api/commit/342eb12b) Bump @nestjs/common from 7.6.7 to 7.6.8 +- [`3d34dc02`](https://github.com/staart/api/commit/3d34dc02) Bump @nestjs/common from 7.6.8 to 7.6.11 +- [`7382ade4`](https://github.com/staart/api/commit/7382ade4) Bump @nestjs/testing from 7.6.8 to 7.6.11 +- [`22a64eaa`](https://github.com/staart/api/commit/22a64eaa) Bump @nestjs/platform-express from 7.6.8 to 7.6.11 +- [`9facb61d`](https://github.com/staart/api/commit/9facb61d) Bump @typescript-eslint/eslint-plugin from 4.14.1 to 4.14.2 +- [`405b1895`](https://github.com/staart/api/commit/405b1895) Bump @octokit/rest from 18.0.15 to 18.1.0 +- [`e9641a7c`](https://github.com/staart/api/commit/e9641a7c) Bump @nestjs/config from 0.6.2 to 0.6.3 + +## v4.0.4 (2021-02-01) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.4) · [💻 Compare](https://github.com/staart/api/compare/v4.0.3...v4.0.4) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.4) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.4.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.4.tar.gz)) + +### ⬆️ Dependency updates + +- [`c039311d`](https://github.com/staart/api/commit/c039311d) Bump @nestjs/config from 0.6.1 to 0.6.2 +- [`810f9e0b`](https://github.com/staart/api/commit/810f9e0b) Bump aws-sdk from 2.830.0 to 2.831.0 +- [`aeb037c9`](https://github.com/staart/api/commit/aeb037c9) Bump aws-elasticsearch-connector from 9.0.1 to 9.0.3 +- [`d281dc80`](https://github.com/staart/api/commit/d281dc80) Bump @nestjs/schedule from 0.4.1 to 0.4.2 +- [`470ad4d6`](https://github.com/staart/api/commit/470ad4d6) Bump supertest from 6.1.1 to 6.1.2 +- [`b4a0dc00`](https://github.com/staart/api/commit/b4a0dc00) Bump p-retry from 4.2.0 to 4.3.0 +- [`7d77fdcb`](https://github.com/staart/api/commit/7d77fdcb) Bump @nestjs/schematics from 7.2.6 to 7.2.7 +- [`7e265f6d`](https://github.com/staart/api/commit/7e265f6d) Bump @octokit/rest from 18.0.13 to 18.0.14 +- [`4bc1ea47`](https://github.com/staart/api/commit/4bc1ea47) Bump @sentry/node from 6.0.1 to 6.0.2 +- [`c2522e92`](https://github.com/staart/api/commit/c2522e92) Bump supertest from 6.1.2 to 6.1.3 +- [`aa8cdb72`](https://github.com/staart/api/commit/aa8cdb72) Bump @typescript-eslint/parser from 4.14.0 to 4.14.1 +- [`35553763`](https://github.com/staart/api/commit/35553763) Bump @typescript-eslint/eslint-plugin from 4.14.0 to 4.14.1 +- [`e9cfda57`](https://github.com/staart/api/commit/e9cfda57) Bump @nestjs/testing from 7.6.5 to 7.6.6 +- [`9c43988d`](https://github.com/staart/api/commit/9c43988d) Bump @nestjs/common from 7.6.5 to 7.6.6 +- [`132933f8`](https://github.com/staart/api/commit/132933f8) Bump @nestjs/core from 7.6.5 to 7.6.6 +- [`86d6d090`](https://github.com/staart/api/commit/86d6d090) Bump @nestjs/core from 7.6.6 to 7.6.7 +- [`87d17644`](https://github.com/staart/api/commit/87d17644) Bump twilio from 3.55.0 to 3.55.1 +- [`1a6bf548`](https://github.com/staart/api/commit/1a6bf548) Bump @nestjs/platform-express from 7.6.5 to 7.6.7 +- [`b158071e`](https://github.com/staart/api/commit/b158071e) Bump @nestjs/common from 7.6.6 to 7.6.7 +- [`838e17e9`](https://github.com/staart/api/commit/838e17e9) Bump aws-sdk from 2.831.0 to 2.832.0 +- [`70f116ac`](https://github.com/staart/api/commit/70f116ac) Bump @nestjs/testing from 7.6.6 to 7.6.7 +- [`3cdb9253`](https://github.com/staart/api/commit/3cdb9253) Bump aws-sdk from 2.832.0 to 2.833.0 + +## v4.0.3 (2021-01-25) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.3) · [💻 Compare](https://github.com/staart/api/compare/v4.0.2...v4.0.3) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.3) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.3.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.3.tar.gz)) + +### ⬆️ Dependency updates + +- [`bd0baca6`](https://github.com/staart/api/commit/bd0baca6) Bump eslint-config-prettier from 7.1.0 to 7.2.0 +- [`92b705ad`](https://github.com/staart/api/commit/92b705ad) Bump @typescript-eslint/parser from 4.13.0 to 4.14.0 +- [`06043340`](https://github.com/staart/api/commit/06043340) Bump helmet from 4.4.0 to 4.4.1 +- [`5463fd71`](https://github.com/staart/api/commit/5463fd71) Bump @typescript-eslint/eslint-plugin from 4.13.0 to 4.14.0 +- [`8df7a8c2`](https://github.com/staart/api/commit/8df7a8c2) Bump aws-sdk from 2.828.0 to 2.829.0 +- [`c2022ccd`](https://github.com/staart/api/commit/c2022ccd) Bump @sentry/node from 5.30.0 to 6.0.0 +- [`f5a380f8`](https://github.com/staart/api/commit/f5a380f8) Bump @types/node from 14.14.21 to 14.14.22 +- [`fdf181df`](https://github.com/staart/api/commit/fdf181df) Bump fs-extra from 9.0.1 to 9.1.0 +- [`1e9cfec9`](https://github.com/staart/api/commit/1e9cfec9) Bump @prisma/cli from 2.14.0 to 2.15.0 +- [`b5ef46c5`](https://github.com/staart/api/commit/b5ef46c5) Bump @googlemaps/google-maps-services-js +- [`616bfb48`](https://github.com/staart/api/commit/616bfb48) Bump @octokit/rest from 18.0.12 to 18.0.13 +- [`6669588a`](https://github.com/staart/api/commit/6669588a) Bump @sentry/node from 6.0.0 to 6.0.1 +- [`8987b207`](https://github.com/staart/api/commit/8987b207) Bump aws-sdk from 2.829.0 to 2.830.0 +- [`c8dd8a62`](https://github.com/staart/api/commit/c8dd8a62) Bump stripe from 8.131.1 to 8.132.0 +- [`e8bdc1e1`](https://github.com/staart/api/commit/e8bdc1e1) Bump @nestjs/swagger from 4.7.11 to 4.7.12 + +## v4.0.2 (2021-01-18) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.2) · [💻 Compare](https://github.com/staart/api/compare/v4.0.1...v4.0.2) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.2) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.2.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.2.tar.gz)) + +### ⬆️ Dependency updates + +- [`12511426`](https://github.com/staart/api/commit/12511426) Bump @types/express from 4.17.9 to 4.17.10 +- [`dea87dc5`](https://github.com/staart/api/commit/dea87dc5) Bump @typescript-eslint/eslint-plugin from 4.12.0 to 4.13.0 +- [`3e244c63`](https://github.com/staart/api/commit/3e244c63) Bump aws-sdk from 2.824.0 to 2.825.0 +- [`b2c9f3c4`](https://github.com/staart/api/commit/b2c9f3c4) Bump @typescript-eslint/parser from 4.12.0 to 4.13.0 +- [`27c2a43d`](https://github.com/staart/api/commit/27c2a43d) Bump aws-sdk from 2.825.0 to 2.826.0 +- [`df090c76`](https://github.com/staart/api/commit/df090c76) Bump @nestjs/swagger from 4.7.9 to 4.7.10 +- [`b0227bd9`](https://github.com/staart/api/commit/b0227bd9) Bump @types/express from 4.17.10 to 4.17.11 +- [`d0ad37ca`](https://github.com/staart/api/commit/d0ad37ca) Bump @slack/web-api from 5.15.0 to 6.0.0 +- [`bf60e861`](https://github.com/staart/api/commit/bf60e861) Bump aws-sdk from 2.826.0 to 2.827.0 +- [`bd83ac32`](https://github.com/staart/api/commit/bd83ac32) Bump twilio from 3.54.2 to 3.55.0 +- [`e4688514`](https://github.com/staart/api/commit/e4688514) Bump @sentry/node from 5.29.2 to 5.30.0 +- [`5e37e70b`](https://github.com/staart/api/commit/5e37e70b) Bump @types/node from 14.14.20 to 14.14.21 +- [`3579cbb0`](https://github.com/staart/api/commit/3579cbb0) Bump @googlemaps/google-maps-services-js +- [`f3ecfad1`](https://github.com/staart/api/commit/f3ecfad1) Bump stripe from 8.130.0 to 8.131.0 +- [`b0c7d6f7`](https://github.com/staart/api/commit/b0c7d6f7) Bump aws-sdk from 2.827.0 to 2.828.0 +- [`172f11b0`](https://github.com/staart/api/commit/172f11b0) Bump class-transformer from 0.3.1 to 0.3.2 +- [`96abb0e2`](https://github.com/staart/api/commit/96abb0e2) Bump supertest from 6.0.1 to 6.1.1 +- [`eb1cfb79`](https://github.com/staart/api/commit/eb1cfb79) Bump stripe from 8.131.0 to 8.131.1 +- [`c5012a6f`](https://github.com/staart/api/commit/c5012a6f) Bump helmet from 4.3.1 to 4.4.0 +- [`0e8c844d`](https://github.com/staart/api/commit/0e8c844d) Bump @nestjs/swagger from 4.7.10 to 4.7.11 +- [`ee45f005`](https://github.com/staart/api/commit/ee45f005) Bump eslint from 7.17.0 to 7.18.0 + +## v4.0.1 (2021-01-11) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.1) · [💻 Compare](https://github.com/staart/api/compare/v4.0.0...v4.0.1) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.1) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.1.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.1.tar.gz)) + +### ♻️ Updates + +- [`9b3629c7`](https://github.com/staart/api/commit/9b3629c7) Add create group to scopes +- [`5d660449`](https://github.com/staart/api/commit/5d660449) Update scope for writing membership + +### ⬆️ Dependency updates + +- [`35aecbdc`](https://github.com/staart/api/commit/35aecbdc) Bump rate-limiter-flexible from 2.1.16 to 2.2.1 + +## v4.0.0 (2021-01-09) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v4.0.0) · [💻 Compare](https://github.com/staart/api/compare/v3.6.17...v4.0.0) · [🔖 Tag](https://github.com/staart/api/tree/v4.0.0) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v4.0.0.zip) · [tar.gz](https://github.com/staart/api/archive/v4.0.0.tar.gz)) + +### ✨ New features + +- [`ec40a981`](https://github.com/staart/api/commit/ec40a981) Add audit logs to users/groups +- [`ede77f40`](https://github.com/staart/api/commit/ede77f40) Add metrics module (#1142) +(Issues: [`#1142`](https://github.com/staart/api/issues/1142))- [`533f690a`](https://github.com/staart/api/commit/533f690a) Update metrics, verify domains, delete audit logs in tasks +- [`a96f8245`](https://github.com/staart/api/commit/a96f8245) Add profile picture, deleting to user +- [`93d9d78b`](https://github.com/staart/api/commit/93d9d78b) Add meta module + +### ♻️ Updates + +- [`10f6cc8f`](https://github.com/staart/api/commit/10f6cc8f) Update configuration +- [`0ebf7425`](https://github.com/staart/api/commit/0ebf7425) Update API keys module, logger +- [`10e7ab82`](https://github.com/staart/api/commit/10e7ab82) Add origin, session ID, role to auth +- [`385c93c9`](https://github.com/staart/api/commit/385c93c9) Update domain module with methods +- [`670e7b31`](https://github.com/staart/api/commit/670e7b31) Update email module with imports +- [`7398bc86`](https://github.com/staart/api/commit/7398bc86) Update group module +- [`fd347eeb`](https://github.com/staart/api/commit/fd347eeb) Update memberships module +- [`0cbe65c8`](https://github.com/staart/api/commit/0cbe65c8) Update MFA module with returns +- [`62ac6266`](https://github.com/staart/api/commit/62ac6266) Use config in Stripe module, add comments +- [`66e63e5e`](https://github.com/staart/api/commit/66e63e5e) Remove playwrite, use puppeteer +- [`0d02e650`](https://github.com/staart/api/commit/0d02e650) Update approved subnet, session, webhook modules +- [`54deaf57`](https://github.com/staart/api/commit/54deaf57) Update services +- [`4d48402b`](https://github.com/staart/api/commit/4d48402b) Add config for S3 profile picture +- [`0e63ccd7`](https://github.com/staart/api/commit/0e63ccd7) Update app module with metrics, puppeteer, exception +- [`b3418d0f`](https://github.com/staart/api/commit/b3418d0f) Make gravatar optional (fixed #1114) +(Issues: [`#1114`](https://github.com/staart/api/issues/1114)) + +### ⬆️ Dependency updates + +- [`9c609b19`](https://github.com/staart/api/commit/9c609b19) Bump @prisma/cli from 2.13.0 to 2.14.0 +- [`6d2db524`](https://github.com/staart/api/commit/6d2db524) Bump aws-sdk from 2.815.0 to 2.822.0 +- [`78c68454`](https://github.com/staart/api/commit/78c68454) Bump helmet from 4.2.0 to 4.3.1 +- [`7f2c0c20`](https://github.com/staart/api/commit/7f2c0c20) Bump @typescript-eslint/eslint-plugin from 4.11.1 to 4.12.0 +- [`7c20f043`](https://github.com/staart/api/commit/7c20f043) Bump @nestjs/swagger from 4.7.6 to 4.7.9 +- [`12bcb6f3`](https://github.com/staart/api/commit/12bcb6f3) Bump @nestjs/common from 7.6.4 to 7.6.5 +- [`f05888c3`](https://github.com/staart/api/commit/f05888c3) Bump eslint from 7.16.0 to 7.17.0 +- [`212099c2`](https://github.com/staart/api/commit/212099c2) Bump @typescript-eslint/parser from 4.10.0 to 4.12.0 + +### 💥 Breaking changes + +- [`4f70e60c`](https://github.com/staart/api/commit/4f70e60c) Update where pipe with filters + +## v3.6.17 (2021-01-04) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v3.6.17) · [💻 Compare](https://github.com/staart/api/compare/v3.6.16...v3.6.17) · [🔖 Tag](https://github.com/staart/api/tree/v3.6.17) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v3.6.17.zip) · [tar.gz](https://github.com/staart/api/archive/v3.6.17.tar.gz)) + +### ⬆️ Dependency updates + +- [`99ff80a0`](https://github.com/staart/api/commit/99ff80a0) Bump akhileshns/heroku-deploy from v3.7.8 to v3.8.8 +- [`78c5160c`](https://github.com/staart/api/commit/78c5160c) Bump @nestjs/core from 7.6.3 to 7.6.5 +- [`ac0e5f65`](https://github.com/staart/api/commit/ac0e5f65) Bump @typescript-eslint/eslint-plugin from 4.11.0 to 4.11.1 + +## v3.6.16 (2020-12-28) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v3.6.16) · [💻 Compare](https://github.com/staart/api/compare/v3.6.15...v3.6.16) · [🔖 Tag](https://github.com/staart/api/tree/v3.6.16) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v3.6.16.zip) · [tar.gz](https://github.com/staart/api/archive/v3.6.16.tar.gz)) + +### ⬆️ Dependency updates + +- [`2e06d1f6`](https://github.com/staart/api/commit/2e06d1f6) Bump @nestjs/platform-express from 7.5.5 to 7.6.3 +- [`94f92f7a`](https://github.com/staart/api/commit/94f92f7a) Bump aws-sdk from 2.807.0 to 2.814.0 +- [`d43ceecd`](https://github.com/staart/api/commit/d43ceecd) Bump vsoch/pull-request-action from 1.0.12 to 1.0.13 +- [`7f2beed6`](https://github.com/staart/api/commit/7f2beed6) Bump aws-sdk from 2.814.0 to 2.815.0 +- [`43329a70`](https://github.com/staart/api/commit/43329a70) Bump typescript from 4.1.2 to 4.1.3 +- [`5a1c3a74`](https://github.com/staart/api/commit/5a1c3a74) Bump pascalgn/automerge-action from v0.12.0 to v0.13.0 +- [`ea54504a`](https://github.com/staart/api/commit/ea54504a) Bump node-notifier from 8.0.0 to 8.0.1 +- [`a1c1e818`](https://github.com/staart/api/commit/a1c1e818) Bump swagger-ui-express from 4.1.5 to 4.1.6 +- [`030388b1`](https://github.com/staart/api/commit/030388b1) Bump eslint from 7.15.0 to 7.16.0 +- [`fe2fe64e`](https://github.com/staart/api/commit/fe2fe64e) Bump @nestjs/common from 7.6.1 to 7.6.4 +- [`22a80d4e`](https://github.com/staart/api/commit/22a80d4e) Bump playwright from 1.7.0 to 1.7.1 +- [`32f67c87`](https://github.com/staart/api/commit/32f67c87) Bump @types/node from 14.14.10 to 14.14.16 +- [`93b8203c`](https://github.com/staart/api/commit/93b8203c) Bump @typescript-eslint/eslint-plugin from 4.10.0 to 4.11.0 + +## v3.6.15 (2020-12-21) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v3.6.15) · [💻 Compare](https://github.com/staart/api/compare/v3.6.14...v3.6.15) · [🔖 Tag](https://github.com/staart/api/tree/v3.6.15) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v3.6.15.zip) · [tar.gz](https://github.com/staart/api/archive/v3.6.15.tar.gz)) + +### ⬆️ Dependency updates + +- [`1d54b8a1`](https://github.com/staart/api/commit/1d54b8a1) Bump nodemailer from 6.4.16 to 6.4.17 +- [`3df56fa8`](https://github.com/staart/api/commit/3df56fa8) Bump firebase-admin from 9.4.1 to 9.4.2 +- [`ff674348`](https://github.com/staart/api/commit/ff674348) Bump @nestjs/swagger from 4.7.5 to 4.7.6 +- [`b12c6e33`](https://github.com/staart/api/commit/b12c6e33) Bump got from 11.8.0 to 11.8.1 +- [`26c6347f`](https://github.com/staart/api/commit/26c6347f) Bump @typescript-eslint/parser from 4.9.1 to 4.10.0 +- [`596bf7ce`](https://github.com/staart/api/commit/596bf7ce) Bump @nestjs/common from 7.5.5 to 7.6.1 +- [`24bf0a77`](https://github.com/staart/api/commit/24bf0a77) Bump @nestjs/core from 7.5.5 to 7.6.1 +- [`ea1e0290`](https://github.com/staart/api/commit/ea1e0290) Bump @prisma/client from 2.12.1 to 2.13.0 +- [`bbf894d5`](https://github.com/staart/api/commit/bbf894d5) Bump playwright from 1.6.2 to 1.7.0 +- [`e0502541`](https://github.com/staart/api/commit/e0502541) Bump @typescript-eslint/eslint-plugin from 4.9.1 to 4.10.0 +- [`ec890edf`](https://github.com/staart/api/commit/ec890edf) Bump @nestjs/cli from 7.5.3 to 7.5.4 +- [`32a57284`](https://github.com/staart/api/commit/32a57284) Bump ts-loader from 8.0.11 to 8.0.12 +- [`17c4c5f2`](https://github.com/staart/api/commit/17c4c5f2) Bump actions/setup-node from v2.1.3 to v2.1.4 +- [`2296a22a`](https://github.com/staart/api/commit/2296a22a) Bump @nestjs/testing from 7.5.5 to 7.6.3 +- [`498512a5`](https://github.com/staart/api/commit/498512a5) Bump rate-limiter-flexible from 2.1.13 to 2.1.14 +- [`4f9f56d6`](https://github.com/staart/api/commit/4f9f56d6) Bump @nestjs/core from 7.6.1 to 7.6.3 +- [`7444e976`](https://github.com/staart/api/commit/7444e976) Bump stripe from 8.127.0 to 8.129.0 + +## v3.6.14 (2020-12-14) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v3.6.14) · [💻 Compare](https://github.com/staart/api/compare/v3.6.13...v3.6.14) · [🔖 Tag](https://github.com/staart/api/tree/v3.6.14) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v3.6.14.zip) · [tar.gz](https://github.com/staart/api/archive/v3.6.14.tar.gz)) + +### ⬆️ Dependency updates + +- [`8743ea89`](https://github.com/staart/api/commit/8743ea89) Bump @nestjs/schematics from 7.2.2 to 7.2.5 +- [`104de030`](https://github.com/staart/api/commit/104de030) Bump @types/jest from 26.0.18 to 26.0.19 +- [`8c28425c`](https://github.com/staart/api/commit/8c28425c) Bump actions/setup-node from v2.1.2 to v2.1.3 + +## v3.6.13 (2020-12-10) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v3.6.13) · [💻 Compare](https://github.com/staart/api/compare/v3.6.12...v3.6.13) · [🔖 Tag](https://github.com/staart/api/tree/v3.6.13) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v3.6.13.zip) · [tar.gz](https://github.com/staart/api/archive/v3.6.13.tar.gz)) + +### 🐛 Bug fixes + +- [`da8bd32c`](https://github.com/staart/api/commit/da8bd32c) Add [*] scope to admins + +### ⬆️ Dependency updates + +- [`60478842`](https://github.com/staart/api/commit/60478842) Bump eslint from 7.14.0 to 7.15.0 +- [`c4c5d483`](https://github.com/staart/api/commit/c4c5d483) Bump aws-sdk from 2.803.0 to 2.804.0 +- [`5c04197c`](https://github.com/staart/api/commit/5c04197c) Bump hibp from 9.0.0 to 9.0.3 +- [`39b7601e`](https://github.com/staart/api/commit/39b7601e) Bump ts-node from 9.1.0 to 9.1.1 +- [`cc025c5b`](https://github.com/staart/api/commit/cc025c5b) Bump @typescript-eslint/eslint-plugin from 4.9.0 to 4.9.1 +- [`00d9497f`](https://github.com/staart/api/commit/00d9497f) Bump aws-sdk from 2.804.0 to 2.805.0 +- [`8b7398e6`](https://github.com/staart/api/commit/8b7398e6) Bump @typescript-eslint/parser from 4.9.0 to 4.9.1 +- [`68a7332e`](https://github.com/staart/api/commit/68a7332e) Bump @types/jest from 26.0.16 to 26.0.18 +- [`d7486ed7`](https://github.com/staart/api/commit/d7486ed7) Bump @octokit/rest from 18.0.11 to 18.0.12 +- [`87aebdbc`](https://github.com/staart/api/commit/87aebdbc) Bump uuid from 8.3.1 to 8.3.2 +- [`7e59c2aa`](https://github.com/staart/api/commit/7e59c2aa) Bump @prisma/cli from 2.12.1 to 2.13.0 +- [`d9e99280`](https://github.com/staart/api/commit/d9e99280) Bump aws-sdk from 2.805.0 to 2.807.0 +- [`c53701b9`](https://github.com/staart/api/commit/c53701b9) Bump @types/ua-parser-js from 0.7.33 to 0.7.35 +- [`7bfdc870`](https://github.com/staart/api/commit/7bfdc870) Bump akhileshns/heroku-deploy from v3.6.8 to v3.7.8 + +## v3.6.12 (2020-12-07) + +[📝 Release notes](https://github.com/staart/api/releases/tag/v3.6.12) · [💻 Compare](https://github.com/staart/api/compare/v3.6.11...v3.6.12) · [🔖 Tag](https://github.com/staart/api/tree/v3.6.12) · 🗄️ Archive ([zip](https://github.com/staart/api/archive/v3.6.12.zip) · [tar.gz](https://github.com/staart/api/archive/v3.6.12.tar.gz)) + +### ⬆️ Dependency updates + +- [`c33e0ad6`](https://github.com/staart/api/commit/c33e0ad6) Bump aws-sdk from 2.800.0 to 2.802.0 +- [`b01e785d`](https://github.com/staart/api/commit/b01e785d) Bump @octokit/rest from 18.0.9 to 18.0.10 +- [`7f543317`](https://github.com/staart/api/commit/7f543317) Bump @types/jest from 26.0.15 to 26.0.16 +- [`4f8ea6ea`](https://github.com/staart/api/commit/4f8ea6ea) Bump @koj/config from 1.2.9 to 1.2.11 +- [`d4c35105`](https://github.com/staart/api/commit/d4c35105) Bump @octokit/rest from 18.0.10 to 18.0.11 +- [`dd082b6c`](https://github.com/staart/api/commit/dd082b6c) Bump stripe from 8.126.0 to 8.127.0 +- [`dd452b40`](https://github.com/staart/api/commit/dd452b40) Bump aws-sdk from 2.802.0 to 2.803.0 +- [`4a571b67`](https://github.com/staart/api/commit/4a571b67) Bump ts-node from 9.0.0 to 9.1.0 + +## [v3.6.11](https://github.com/staart/api/compare/v3.6.10...v3.6.11) (2020-12-02) + +### ♻️ Updates + +- [`f8eb9cab`](https://github.com/staart/api/commit/f8eb9cab) Use frontendUrl from meta config +- [`d2ea77b0`](https://github.com/staart/api/commit/d2ea77b0) Use Configuration interface in service + +### ⬆️ Dependency updates + +- [`bb2181f1`](https://github.com/staart/api/commit/bb2181f1) Bump aws-sdk from 2.799.0 to 2.800.0 +- [`6b07b4c4`](https://github.com/staart/api/commit/6b07b4c4) Bump @typescript-eslint/eslint-plugin from 4.8.2 to 4.9.0 +- [`7db658c6`](https://github.com/staart/api/commit/7db658c6) Bump @typescript-eslint/parser from 4.8.2 to 4.9.0 + +## [v3.6.10](https://github.com/staart/api/compare/v3.6.9...v3.6.10) (2020-11-30) + +### ♻️ Updates + +- [`373f40d1`](https://github.com/staart/api/commit/373f40d1) Use acct:URL for access token +- [`eb98a1fb`](https://github.com/staart/api/commit/eb98a1fb) Listen on env PORT + +### 🐛 Bug fixes + +- [`956bfb80`](https://github.com/staart/api/commit/956bfb80) Remove extra # from UI colors + +### ⬆️ Dependency updates + +- [`5a37720b`](https://github.com/staart/api/commit/5a37720b) Bump prettier from 2.2.0 to 2.2.1 + +## [v3.6.9](https://github.com/staart/api/compare/v3.6.8...v3.6.9) (2020-11-30) + +### ⬆️ Dependency updates + +- [`e06499e3`](https://github.com/staart/api/commit/e06499e3) Bump @slack/web-api from 5.13.0 to 5.14.0 +- [`01949b33`](https://github.com/staart/api/commit/01949b33) Bump @prisma/client from 2.12.0 to 2.12.1 +- [`364a477a`](https://github.com/staart/api/commit/364a477a) Bump @prisma/cli from 2.12.0 to 2.12.1 + +## [v3.6.8](https://github.com/staart/api/compare/v3.6.7...v3.6.8) (2020-11-26) + +### 🐛 Bug fixes + +- [`4c5c427e`](https://github.com/staart/api/commit/4c5c427e) Ignore auth if user is not found + +## [v3.6.7](https://github.com/staart/api/compare/v3.6.6...v3.6.7) (2020-11-25) + +### ♻️ Updates + +- [`5afc6d23`](https://github.com/staart/api/commit/5afc6d23) Add Slack method to message channel + +## [v3.6.6](https://github.com/staart/api/compare/v3.6.5...v3.6.6) (2020-11-25) + +### 🐛 Bug fixes + +- [`553d08cd`](https://github.com/staart/api/commit/553d08cd) Fix defaults for Twilio service + +### ⬆️ Dependency updates + +- [`5eb74986`](https://github.com/staart/api/commit/5eb74986) Bump stripe from 8.125.0 to 8.126.0 +- [`44136726`](https://github.com/staart/api/commit/44136726) Bump @types/node from 14.14.9 to 14.14.10 +- [`919bf7da`](https://github.com/staart/api/commit/919bf7da) Bump @nestjs/passport from 7.1.3 to 7.1.5 + +## [v3.6.5](https://github.com/staart/api/compare/v3.6.4...v3.6.5) (2020-11-25) + +### ♻️ Updates + +- [`9ab66dfb`](https://github.com/staart/api/commit/9ab66dfb) Use prisma code references + +### 🐛 Bug fixes + +- [`d63790e1`](https://github.com/staart/api/commit/d63790e1) Fix Prisma update changes + +### ⬆️ Dependency updates + +- [`fd366850`](https://github.com/staart/api/commit/fd366850) Update @prisma/client, @prisma/server to v2.12.0 + +## [v3.6.4](https://github.com/staart/api/compare/v3.6.3...v3.6.4) (2020-11-23) + +## [v3.6.3](https://github.com/staart/api/compare/v3.6.2...v3.6.3) (2020-11-18) + +### ♻️ Updates + +- [`76181f1a`](https://github.com/staart/api/commit/76181f1a) Add Stripe controllers to module +- [`018837c4`](https://github.com/staart/api/commit/018837c4) Add billing portal session link +- [`dba69e18`](https://github.com/staart/api/commit/dba69e18) Redirect to session home +- [`aa64deef`](https://github.com/staart/api/commit/aa64deef) Use GET method for billing portal + +### 🐛 Bug fixes + +- [`a8011fc1`](https://github.com/staart/api/commit/a8011fc1) Make req.user optional +- [`79ddd033`](https://github.com/staart/api/commit/79ddd033) Use /billing/link URL for billing portal + +### ⬆️ Dependency updates + +- [`c2637fad`](https://github.com/staart/api/commit/c2637fad) Update koj-co/template + +## [v3.6.2](https://github.com/staart/api/compare/v3.6.1...v3.6.2) (2020-11-17) + +### ♻️ Updates + +- [`58971f05`](https://github.com/staart/api/commit/58971f05) Use internal interceptor, decorator +- [`c6ba4afd`](https://github.com/staart/api/commit/c6ba4afd) Use custom rate limiter + +## [v3.6.1](https://github.com/staart/api/compare/v3.6.0...v3.6.1) (2020-11-17) + +### 🐛 Bug fixes + +- [`d1e7c654`](https://github.com/staart/api/commit/d1e7c654) Remove unauthorized scopes from API keys + +## [v3.6.0](https://github.com/staart/api/compare/v3.5.6...v3.6.0) (2020-11-16) + +### ✨ New features + +- [`666e37eb`](https://github.com/staart/api/commit/666e37eb) Add endpoint for subgroups + +### ♻️ Updates + +- [`ef2dc724`](https://github.com/staart/api/commit/ef2dc724) Get subgroup scopes on login +- [`c5c54c45`](https://github.com/staart/api/commit/c5c54c45) Add select/include pipe +- [`2adfc21b`](https://github.com/staart/api/commit/2adfc21b) Use create group helper in membership service +- [`4dd62efc`](https://github.com/staart/api/commit/4dd62efc) Remove attributes from DTO +- [`9a9cca5d`](https://github.com/staart/api/commit/9a9cca5d) Use /auth/link for token links +- [`0c2b15b3`](https://github.com/staart/api/commit/0c2b15b3) Move merging accounts to auth.service +- [`70c5328b`](https://github.com/staart/api/commit/70c5328b) Send token response on email verification + +### 🐛 Bug fixes + +- [`69a88ce7`](https://github.com/staart/api/commit/69a88ce7) Return created membership with group +- [`3f9bbaef`](https://github.com/staart/api/commit/3f9bbaef) Use email ID to verify, not user ID +- [`ed3a2103`](https://github.com/staart/api/commit/ed3a2103) Combine specific keys of users, fix response + +## [v3.5.6](https://github.com/staart/api/compare/v3.5.5...v3.5.6) (2020-11-15) + +### ♻️ Updates + +- [`d0a4aceb`](https://github.com/staart/api/commit/d0a4aceb) Check for primary email on deleting emails +- [`1777332d`](https://github.com/staart/api/commit/1777332d) Auto-join groups based on email address +- [`5abcc56d`](https://github.com/staart/api/commit/5abcc56d) Use explicit ID type in params +- [`c499a62c`](https://github.com/staart/api/commit/c499a62c) Store parsed details in session, Change Mac OS -> macOS +- [`e54ddd9b`](https://github.com/staart/api/commit/e54ddd9b) Allow cursors with implicit ID +- [`cf873b1a`](https://github.com/staart/api/commit/cf873b1a) Use colon instead of space in order-by pipe + +### 🐛 Bug fixes + +- [`150f42f0`](https://github.com/staart/api/commit/150f42f0) Ensure there is at least 1 owner in group +- [`3d73c616`](https://github.com/staart/api/commit/3d73c616) Use groupId request param +- [`1804bf39`](https://github.com/staart/api/commit/1804bf39) Render mustache before setting subject +- [`c3dcb4a5`](https://github.com/staart/api/commit/c3dcb4a5) Fix scopes for user, groups routes +- [`3c645992`](https://github.com/staart/api/commit/3c645992) Validate domain name +- [`3175c42d`](https://github.com/staart/api/commit/3175c42d) Only add Stripe scopes if account exists + +## [v3.5.5](https://github.com/staart/api/compare/v3.5.4...v3.5.5) (2020-11-15) + +### 🐛 Bug fixes + +- [`cfe9855a`](https://github.com/staart/api/commit/cfe9855a) Return user info on update/delete + +## [v3.5.4](https://github.com/staart/api/compare/v3.5.3...v3.5.4) (2020-11-15) + +### 🐛 Bug fixes + +- [`bacd1e0b`](https://github.com/staart/api/commit/bacd1e0b) Include user in membership response + +## [v3.5.3](https://github.com/staart/api/compare/v3.5.2...v3.5.3) (2020-11-15) + +### ♻️ Updates + +- [`ed3b942e`](https://github.com/staart/api/commit/ed3b942e) Delete sessions on deactivate + +## [v3.5.2](https://github.com/staart/api/compare/v3.5.1...v3.5.2) (2020-11-15) + +### 🐛 Bug fixes + +- [`a8c75f8b`](https://github.com/staart/api/commit/a8c75f8b) Merge requests cannot go for the same user + +## [v3.5.1](https://github.com/staart/api/compare/v3.5.0...v3.5.1) (2020-11-15) + +### 🐛 Bug fixes + +- [`95678056`](https://github.com/staart/api/commit/95678056) Validate new email + +## [v3.5.0](https://github.com/staart/api/compare/v3.4.0...v3.5.0) (2020-11-14) + +### ✨ New features + +- [`de4dd7a6`](https://github.com/staart/api/commit/de4dd7a6) Add Google Maps module +- [`7d1c2918`](https://github.com/staart/api/commit/7d1c2918) Add Playwrite module + +### ♻️ Updates + +- [`a7197cb5`](https://github.com/staart/api/commit/a7197cb5) Rename service clients to `client` +- [`163f170e`](https://github.com/staart/api/commit/163f170e) Use tokensService instead of jwtService +- [`16afe470`](https://github.com/staart/api/commit/16afe470) Add default boolean configuration values + +## [v3.4.0](https://github.com/staart/api/compare/v3.3.1...v3.4.0) (2020-11-13) + +### ✨ New features + +- [`99f17c8e`](https://github.com/staart/api/commit/99f17c8e) Add Slack module +- [`bd930bec`](https://github.com/staart/api/commit/bd930bec) Add Airtable module +- [`7bc5e7c6`](https://github.com/staart/api/commit/7bc5e7c6) Add AWS S3 service +- [`37c1eb8b`](https://github.com/staart/api/commit/37c1eb8b) Add Cloudinary module +- [`835eb270`](https://github.com/staart/api/commit/835eb270) Add Firebase module +- [`a0eff5a8`](https://github.com/staart/api/commit/a0eff5a8) Add GitHub module + +## [v3.3.1](https://github.com/staart/api/compare/v3.3.0...v3.3.1) (2020-11-13) + +### ♻️ Updates + +- [`841ffef9`](https://github.com/staart/api/commit/841ffef9) Add endpoints for API key logs +- [`f50a401c`](https://github.com/staart/api/commit/f50a401c) Support logging authenticated requests + +### 🐛 Bug fixes + +- [`b9b30146`](https://github.com/staart/api/commit/b9b30146) Ensure API key is a UUID + +## [v3.3.0](https://github.com/staart/api/compare/v3.2.0...v3.3.0) (2020-11-13) + +### ✨ New features + +- [`bbb002f7`](https://github.com/staart/api/commit/bbb002f7) Log API requests in ElasticSearch +- [`89b7bd2c`](https://github.com/staart/api/commit/89b7bd2c) Delete old API logs + +### ♻️ Updates + +- [`a761d017`](https://github.com/staart/api/commit/a761d017) Use Authorization header instead of X-Api-Key + +### 🐛 Bug fixes + +- [`b73cd6aa`](https://github.com/staart/api/commit/b73cd6aa) Return API key data from LRU if available + +## [v3.2.0](https://github.com/staart/api/compare/v3.1.2...v3.2.0) (2020-11-12) + +### ✨ New features + +- [`4631bbb8`](https://github.com/staart/api/commit/4631bbb8) Add ElasticSearch service + +## [v3.1.2](https://github.com/staart/api/compare/v3.1.1...v3.1.2) (2020-11-10) + +### ♻️ Updates + +- [`8e896920`](https://github.com/staart/api/commit/8e896920) Add configuration for retries + +### ⬆️ Dependency updates + +- [`c03fb8d8`](https://github.com/staart/api/commit/c03fb8d8) Update aws-sdk, stripe + +## [v3.1.1](https://github.com/staart/api/compare/v3.1.0...v3.1.1) (2020-11-09) + +### ♻️ Updates + +- [`fb081a2c`](https://github.com/staart/api/commit/fb081a2c) Allow email configuration of SES +- [`d576cf0e`](https://github.com/staart/api/commit/d576cf0e) Change required config in SES + +## [v3.1.0](https://github.com/staart/api/compare/v3.0.1...v3.1.0) (2020-11-09) + +### ✨ New features + +- [`48ba1763`](https://github.com/staart/api/commit/48ba1763) Add account deactivate (fixed #1350) +(Issues: [`#1350`](https://github.com/staart/api/issues/1350)) + +### ♻️ Updates + +- [`dc1a3704`](https://github.com/staart/api/commit/dc1a3704) Auto-set account to active on login + +## [v3.0.1](https://github.com/staart/api/compare/v3.0.0...v3.0.1) (2020-11-08) + +### 🐛 Bug fixes + +- [`ac17d989`](https://github.com/staart/api/commit/ac17d989) Fix CWE-20 in URL parsing + +## [v3.0.0](https://github.com/staart/api/compare/v2.21.0...v3.0.0) (2020-11-08) + +### ✨ New features + +- [`47ceb5bc`](https://github.com/staart/api/commit/47ceb5bc) Add support for login links +- [`161b2643`](https://github.com/staart/api/commit/161b2643) Add endpoint for password details +- [`ef5654db`](https://github.com/staart/api/commit/ef5654db) Ship Casbin-powered permissions (fixed #337) +(Issues: [`#337`](https://github.com/staart/api/issues/337))- [`872559c6`](https://github.com/staart/api/commit/872559c6) Add user access token scopes endpoint +- [`5225c309`](https://github.com/staart/api/commit/5225c309) Add API scopes endpoint +- [`8de94323`](https://github.com/staart/api/commit/8de94323) Add gender prediction API +- [`e9baebd9`](https://github.com/staart/api/commit/e9baebd9) Auto-fill country, timezone +- [`cbb355ee`](https://github.com/staart/api/commit/cbb355ee) Add Sentry +- [`ff44de69`](https://github.com/staart/api/commit/ff44de69) Add support for disabling billing +- [`b248d84c`](https://github.com/staart/api/commit/b248d84c) Add new user registrations check +- [`54774f25`](https://github.com/staart/api/commit/54774f25) Add newUserRegistrationDomains check +- [`d6beaf83`](https://github.com/staart/api/commit/d6beaf83) Add Prisma CRUD endpoints +- [`b1074976`](https://github.com/staart/api/commit/b1074976) Add pipes for optional int, order by +- [`6b732e2a`](https://github.com/staart/api/commit/6b732e2a) Add support for cursor +- [`6d43fcfe`](https://github.com/staart/api/commit/6d43fcfe) Use DTO in PATCH method +- [`c9f03df3`](https://github.com/staart/api/commit/c9f03df3) Add auth module with register +- [`c9291532`](https://github.com/staart/api/commit/c9291532) Add registration with email conflict check +- [`b311e5af`](https://github.com/staart/api/commit/b311e5af) Add common configuration +- [`7b6902d8`](https://github.com/staart/api/commit/7b6902d8) Render and send emails +- [`9fe994d3`](https://github.com/staart/api/commit/9fe994d3) Add HTML email layout +- [`a7979c22`](https://github.com/staart/api/commit/a7979c22) Add resend email verification endpoint +- [`3b4a4680`](https://github.com/staart/api/commit/3b4a4680) Add authentication +- [`5abd4987`](https://github.com/staart/api/commit/5abd4987) Add refresh token endpoint +- [`d1e9e252`](https://github.com/staart/api/commit/d1e9e252) Add scope authorization in Guard +- [`93d82e1c`](https://github.com/staart/api/commit/93d82e1c) Expose data by removing secrets +- [`0e7c8b20`](https://github.com/staart/api/commit/0e7c8b20) Add session endpoints +- [`8233d52a`](https://github.com/staart/api/commit/8233d52a) Add endpoints for access tokens +- [`276c95a3`](https://github.com/staart/api/commit/276c95a3) Add endpoints for user memberships +- [`f7082e0f`](https://github.com/staart/api/commit/f7082e0f) Add emails module +- [`818ad11a`](https://github.com/staart/api/commit/818ad11a) Add groups endpoints +- [`26c0c0ef`](https://github.com/staart/api/commit/26c0c0ef) Add group membership controller +- [`a642b7ea`](https://github.com/staart/api/commit/a642b7ea) Support creating groups, memberships +- [`183b6749`](https://github.com/staart/api/commit/183b6749) Add Pwned module +- [`21a7cb4f`](https://github.com/staart/api/commit/21a7cb4f) Add support for password change, refactor auth +- [`d99de49c`](https://github.com/staart/api/commit/d99de49c) Add scheduler to delete sessions +- [`b3ec3fc4`](https://github.com/staart/api/commit/b3ec3fc4) Add helmet for security +- [`754495e1`](https://github.com/staart/api/commit/754495e1) Add OpenAPI docs +- [`e3628898`](https://github.com/staart/api/commit/e3628898) Add tokens module, 2FA +- [`9aaee67a`](https://github.com/staart/api/commit/9aaee67a) Add logout endpoint +- [`cee3a55c`](https://github.com/staart/api/commit/cee3a55c) Add 2FA enable/disable endpoints +- [`f8f47f29`](https://github.com/staart/api/commit/f8f47f29) Add password forgot/reset +- [`339a29da`](https://github.com/staart/api/commit/339a29da) Add verify emails endpoint +- [`e4e78e1d`](https://github.com/staart/api/commit/e4e78e1d) Add approved subnets endpoints +- [`b3f60938`](https://github.com/staart/api/commit/b3f60938) Add geolocation service +- [`2c892e83`](https://github.com/staart/api/commit/2c892e83) Add approve subnet endpoint +- [`0f219cdc`](https://github.com/staart/api/commit/0f219cdc) Add support for MFA when logging in +- [`e314375b`](https://github.com/staart/api/commit/e314375b) Login with email token endpoints +- [`8c7f926b`](https://github.com/staart/api/commit/8c7f926b) Send membership welcome email +- [`9857fbc5`](https://github.com/staart/api/commit/9857fbc5) Support logging in backup code +- [`f1337775`](https://github.com/staart/api/commit/f1337775) Add group API keys module +- [`62ee163d`](https://github.com/staart/api/commit/62ee163d) Add basic Stripe module +- [`0dc4b422`](https://github.com/staart/api/commit/0dc4b422) Add Stripe invoices endpoints +- [`ec9ec37b`](https://github.com/staart/api/commit/ec9ec37b) Add Twilio module +- [`ce710401`](https://github.com/staart/api/commit/ce710401) Add SMS MFA OTP +- [`b147b685`](https://github.com/staart/api/commit/b147b685) Add email MFA +- [`f39eddac`](https://github.com/staart/api/commit/f39eddac) Add Stripe sources endpoints +- [`74df85c2`](https://github.com/staart/api/commit/74df85c2) Add API key scopes +- [`2a8170f1`](https://github.com/staart/api/commit/2a8170f1) Add domain module +- [`59a4a7cb`](https://github.com/staart/api/commit/59a4a7cb) Add DNS module +- [`e160f8ef`](https://github.com/staart/api/commit/e160f8ef) Add HTML domain verification +- [`9da99653`](https://github.com/staart/api/commit/9da99653) Add subscription endpoints +- [`7506cb20`](https://github.com/staart/api/commit/7506cb20) Handle Stripe webhook event +- [`049e3eb0`](https://github.com/staart/api/commit/049e3eb0) Add audit logs module +- [`36bda486`](https://github.com/staart/api/commit/36bda486) Create audit log +- [`47869cdd`](https://github.com/staart/api/commit/47869cdd) Add webhooks module +- [`53ebb4a8`](https://github.com/staart/api/commit/53ebb4a8) Trigger webhooks on audit log +- [`8a5c0152`](https://github.com/staart/api/commit/8a5c0152) Implement LRU for API keys +- [`bf9976d8`](https://github.com/staart/api/commit/bf9976d8) Add API key users controllers +- [`7c35bd21`](https://github.com/staart/api/commit/7c35bd21) Serve static files +- [`cbc8034c`](https://github.com/staart/api/commit/cbc8034c) Add support for merging users (fixed #950) +(Issues: [`#950`](https://github.com/staart/api/issues/950))- [`727b6112`](https://github.com/staart/api/commit/727b6112) Add SMS-based MFA method + +### ♻️ Updates + +- [`3dadf22f`](https://github.com/staart/api/commit/3dadf22f) Change snake to camel case +- [`e7ab0eb7`](https://github.com/staart/api/commit/e7ab0eb7) Change organization to group +- [`5cd080ea`](https://github.com/staart/api/commit/5cd080ea) Update organization group +- [`9691e797`](https://github.com/staart/api/commit/9691e797) Update user service references +- [`dd86fb40`](https://github.com/staart/api/commit/dd86fb40) Update org +- [`efbc78f9`](https://github.com/staart/api/commit/efbc78f9) Organize imports, update user rest +- [`cabd9e13`](https://github.com/staart/api/commit/cabd9e13) Make login password optional +- [`8b2e8e12`](https://github.com/staart/api/commit/8b2e8e12) Use object param for mail +- [`6b09c3e3`](https://github.com/staart/api/commit/6b09c3e3) Allow all attributes in mail +- [`4dc4bf69`](https://github.com/staart/api/commit/4dc4bf69) Update helpers +- [`91ac3cc6`](https://github.com/staart/api/commit/91ac3cc6) Use TWT instead of username +- [`3f3ccf14`](https://github.com/staart/api/commit/3f3ccf14) Use number for ID, not string +- [`8cae2670`](https://github.com/staart/api/commit/8cae2670) Remove fallback from TWT +- [`0efe1c8f`](https://github.com/staart/api/commit/0efe1c8f) Use number instead of string in ID +- [`1a7a1181`](https://github.com/staart/api/commit/1a7a1181) Use number IDs in controllers +- [`170ba999`](https://github.com/staart/api/commit/170ba999) Add login link token to email +- [`5edf233a`](https://github.com/staart/api/commit/5edf233a) Use number ID for user, validate number +- [`a3535905`](https://github.com/staart/api/commit/a3535905) Use number for org ID +- [`2c201062`](https://github.com/staart/api/commit/2c201062) Use TWT for IDs +- [`e70fa98c`](https://github.com/staart/api/commit/e70fa98c) Use TWT of length 10 +- [`8327891f`](https://github.com/staart/api/commit/8327891f) Support all id-like keys with TWT +- [`bba4a39a`](https://github.com/staart/api/commit/bba4a39a) Allow empty passwords +- [`ae0022b9`](https://github.com/staart/api/commit/ae0022b9) Add decode TWT function +- [`5a6fa2e7`](https://github.com/staart/api/commit/5a6fa2e7) Use string for ID validation +- [`08a2c53a`](https://github.com/staart/api/commit/08a2c53a) Use Joi.number() for ID +- [`8c90ee64`](https://github.com/staart/api/commit/8c90ee64) use take in rest +- [`d598a1f1`](https://github.com/staart/api/commit/d598a1f1) Use any for res.locals +- [`9558c36d`](https://github.com/staart/api/commit/9558c36d) Use take in Prisma +- [`905e0181`](https://github.com/staart/api/commit/905e0181) Use new authorization helper in user.ts +- [`9301944a`](https://github.com/staart/api/commit/9301944a) Add Casbin admin scopes +- [`02799be5`](https://github.com/staart/api/commit/02799be5) Use new authorization can in group, auth +- [`28b0b142`](https://github.com/staart/api/commit/28b0b142) Change params to subject, action, object +- [`cd1fb73e`](https://github.com/staart/api/commit/cd1fb73e) Remove expiry from access tokens +- [`fefcdba2`](https://github.com/staart/api/commit/fefcdba2) Move access token scopes to security +- [`2a75733f`](https://github.com/staart/api/commit/2a75733f) Use constants in policy +- [`13490a14`](https://github.com/staart/api/commit/13490a14) Update delete casbin policies +- [`3fac6c93`](https://github.com/staart/api/commit/3fac6c93) Remove username validation +- [`63577fad`](https://github.com/staart/api/commit/63577fad) Change API key, access token length to 32 +- [`629b7aac`](https://github.com/staart/api/commit/629b7aac) Update group ID as attribute +- [`1e040a76`](https://github.com/staart/api/commit/1e040a76) Change stripeCustomerId to stripeCustomer +- [`9b760e1d`](https://github.com/staart/api/commit/9b760e1d) Skip test for Stripe +- [`cd433d17`](https://github.com/staart/api/commit/cd433d17) Make tracking optional +- [`d83146ce`](https://github.com/staart/api/commit/d83146ce) Check tracking config before ES +- [`0f300e3b`](https://github.com/staart/api/commit/0f300e3b) Move some config from file +- [`733f923d`](https://github.com/staart/api/commit/733f923d) Use config helpr instead of imports +- [`b4c6176d`](https://github.com/staart/api/commit/b4c6176d) Use email config with Nodemailer interface +- [`34eb080f`](https://github.com/staart/api/commit/34eb080f) Use session UUID as refresh token +- [`527ab9a3`](https://github.com/staart/api/commit/527ab9a3) Get user ID, scopes in JWT strategy +- [`3e259b43`](https://github.com/staart/api/commit/3e259b43) Use access toke ngenerator abstraction +- [`6a31e886`](https://github.com/staart/api/commit/6a31e886) Use local scope guards (https://stackoverflow.com/a/50801832/1656944) +- [`d30ce2fa`](https://github.com/staart/api/commit/d30ce2fa) Use session ID as param +- [`959dc7b8`](https://github.com/staart/api/commit/959dc7b8) Use global guards, @Public decorator +- [`be84f02f`](https://github.com/staart/api/commit/be84f02f) Use new scope structure +- [`915c5ae6`](https://github.com/staart/api/commit/915c5ae6) Send email not verified exception message +- [`b7cf9f39`](https://github.com/staart/api/commit/b7cf9f39) Hash passwords, ensure uncompromised +- [`62d6ae04`](https://github.com/staart/api/commit/62d6ae04) Use prisma directly not authService +- [`f7697ebd`](https://github.com/staart/api/commit/f7697ebd) Use safe email helper +- [`75fc733f`](https://github.com/staart/api/commit/75fc733f) Lowercase, remove plus from email +- [`e69cc370`](https://github.com/staart/api/commit/e69cc370) Use import instead of import type +- [`750eb880`](https://github.com/staart/api/commit/750eb880) Change 2fa to totp +- [`0857ae77`](https://github.com/staart/api/commit/0857ae77) Use auth module-scoped constants +- [`3fb490fa`](https://github.com/staart/api/commit/3fb490fa) Change approved location -> approved subnet +- [`fe973961`](https://github.com/staart/api/commit/fe973961) Hash approved subnets +- [`d0bb693c`](https://github.com/staart/api/commit/d0bb693c) Add cache to geolocation +- [`d9899226`](https://github.com/staart/api/commit/d9899226) Store geolocation in approved subnets +- [`24afea03`](https://github.com/staart/api/commit/24afea03) Approve new subnets in auth +- [`63bac411`](https://github.com/staart/api/commit/63bac411) Allow adding team members without name +- [`ffd8fdf8`](https://github.com/staart/api/commit/ffd8fdf8) Generate/regenerate backup codes in 2FA +- [`e3349735`](https://github.com/staart/api/commit/e3349735) Add email verification template +- [`d4c10d29`](https://github.com/staart/api/commit/d4c10d29) Add create/delete/replace customer endpoint +- [`560c03ac`](https://github.com/staart/api/commit/560c03ac) Allow uppercase sorting +- [`6c2897da`](https://github.com/staart/api/commit/6c2897da) Use relative import paths +- [`170bc5de`](https://github.com/staart/api/commit/170bc5de) Use .env data in configuration +- [`20d8cebb`](https://github.com/staart/api/commit/20d8cebb) Change twoFactorEnabled -> twoFactorMethod +- [`b5d94ce7`](https://github.com/staart/api/commit/b5d94ce7) Use TokensService for UUID +- [`b490af16`](https://github.com/staart/api/commit/b490af16) Update scopes in controllers +- [`3253de5b`](https://github.com/staart/api/commit/3253de5b) Normalize domain URL +- [`c401a330`](https://github.com/staart/api/commit/c401a330) Normalize +, . in emails +- [`c0b928a8`](https://github.com/staart/api/commit/c0b928a8) Use raw/JSON middleware +- [`c797457b`](https://github.com/staart/api/commit/c797457b) Change auth controller login routes +- [`aa80db1b`](https://github.com/staart/api/commit/aa80db1b) Use constant for login token sub +- [`a39009b7`](https://github.com/staart/api/commit/a39009b7) Add membership module to app +- [`27066a69`](https://github.com/staart/api/commit/27066a69) Add pretty profile pictures for domain, group, user +- [`86a456d1`](https://github.com/staart/api/commit/86a456d1) Add ID to auth token +- [`43e85967`](https://github.com/staart/api/commit/43e85967) Use Gravatar as user profile picture +- [`5a1cb701`](https://github.com/staart/api/commit/5a1cb701) Add audit log decorators on controller +- [`74705d67`](https://github.com/staart/api/commit/74705d67) Add webhook scopes to API key +- [`6c546cfa`](https://github.com/staart/api/commit/6c546cfa) Add webhook scopes endpoint +- [`89af8027`](https://github.com/staart/api/commit/89af8027) Add group/user methods in API kes +- [`c84bccba`](https://github.com/staart/api/commit/c84bccba) Add API key scopes for user +- [`24f0d246`](https://github.com/staart/api/commit/24f0d246) Only allow clean, secure scopes in API keys +- [`b32c5af3`](https://github.com/staart/api/commit/b32c5af3) Use custom JWT strategy +- [`49ed4a47`](https://github.com/staart/api/commit/49ed4a47) Support referrer restrictions in API keys +- [`e24b26d0`](https://github.com/staart/api/commit/e24b26d0) Check IP address restrictions in API keys +- [`269d1f17`](https://github.com/staart/api/commit/269d1f17) Rename jwt -> staart in auth +- [`500534bf`](https://github.com/staart/api/commit/500534bf) Use user object in auth objects +- [`149651c2`](https://github.com/staart/api/commit/149651c2) Use service name in loggers +- [`8ac6f7f3`](https://github.com/staart/api/commit/8ac6f7f3) Send response time headers +- [`8e248ffa`](https://github.com/staart/api/commit/8e248ffa) Don't use native errors +- [`402ce140`](https://github.com/staart/api/commit/402ce140) Don't use HttpException +- [`a7ee700f`](https://github.com/staart/api/commit/a7ee700f) Use error constants +- [`91aba2e3`](https://github.com/staart/api/commit/91aba2e3) Add constants errors in pipes +- [`d0e58468`](https://github.com/staart/api/commit/d0e58468) Add descriptions for errors +- [`52e5de85`](https://github.com/staart/api/commit/52e5de85) Add global controller prefix + +### 🐛 Bug fixes + +- [`cf4a8fd7`](https://github.com/staart/api/commit/cf4a8fd7) Wait for token to be generated +- [`128d995b`](https://github.com/staart/api/commit/128d995b) Use Tokens.LOGIN_LINK to verify JWT +- [`661bf6d0`](https://github.com/staart/api/commit/661bf6d0) Use string for userId in TWT +- [`75fc64fc`](https://github.com/staart/api/commit/75fc64fc) Use take instead of first +- [`97b21181`](https://github.com/staart/api/commit/97b21181) Use TWT in local to token +- [`780e7d6b`](https://github.com/staart/api/commit/780e7d6b) Use where with ID key +- [`aea4d9f0`](https://github.com/staart/api/commit/aea4d9f0) Use numbers not TWTs in controllers +- [`b3f2f9bc`](https://github.com/staart/api/commit/b3f2f9bc) Use string adapter for casbin model +- [`294a5682`](https://github.com/staart/api/commit/294a5682) Use integer IDs, not TWTs, in casbin policy +- [`14f3182e`](https://github.com/staart/api/commit/14f3182e) Change scopes type in access token, API key +- [`17f07562`](https://github.com/staart/api/commit/17f07562) Make sure user has a verified email +- [`dc67971e`](https://github.com/staart/api/commit/dc67971e) Only allow admin, member roles in teams +- [`4d070cf1`](https://github.com/staart/api/commit/4d070cf1) Create memberships manually +- [`27baa6ec`](https://github.com/staart/api/commit/27baa6ec) Allow prefers email ID +- [`3a0f0c4d`](https://github.com/staart/api/commit/3a0f0c4d) Change profilePicture to Url suffix +- [`87c9b13e`](https://github.com/staart/api/commit/87c9b13e) Fix stripe customer ID key +- [`d3c8c255`](https://github.com/staart/api/commit/d3c8c255) Change imports to config +- [`5e6d6918`](https://github.com/staart/api/commit/5e6d6918) Make keys in DTO optional +- [`f27c6b9d`](https://github.com/staart/api/commit/f27c6b9d) Ensure result exists before accessing user +- [`a166eaa1`](https://github.com/staart/api/commit/a166eaa1) Fix use authentication in class +- [`9cdea097`](https://github.com/staart/api/commit/9cdea097) Specify IP address when adding member, add module +- [`97096b3f`](https://github.com/staart/api/commit/97096b3f) Use object payload for JWT +- [`99f9058d`](https://github.com/staart/api/commit/99f9058d) Fix registration, location +- [`10a4b3c0`](https://github.com/staart/api/commit/10a4b3c0) Fix password hash in Pwned +- [`087701ea`](https://github.com/staart/api/commit/087701ea) Use module imports, not service +- [`fb46eeb4`](https://github.com/staart/api/commit/fb46eeb4) Import StripeModule +- [`f883155b`](https://github.com/staart/api/commit/f883155b) Move scopes endpoint to top +- [`e2541a16`](https://github.com/staart/api/commit/e2541a16) Add ConfigModule to ApiKeysModule + +### 🔒 Security issues + +- [`e1d3e0cb`](https://github.com/staart/api/commit/e1d3e0cb) Add AuthGuard in user endpoints +- [`653ccfc4`](https://github.com/staart/api/commit/653ccfc4) Add scopes and guards on user routes + +### ⬆️ Dependency updates + +- [`5693784b`](https://github.com/staart/api/commit/5693784b) Update @staart/redis +- [`c38e0464`](https://github.com/staart/api/commit/c38e0464) Update @staart/redis to v2.3.0 +- [`b9e775fd`](https://github.com/staart/api/commit/b9e775fd) Update @staart/scripts to v1.17.0 +- [`713dc5af`](https://github.com/staart/api/commit/713dc5af) Update @staart/payments to v4.0.0 +- [`59f2ec6b`](https://github.com/staart/api/commit/59f2ec6b) Update @prisma to v2.4.1 +- [`6e121dc5`](https://github.com/staart/api/commit/6e121dc5) Update twt to v1.2.0 +- [`71f81595`](https://github.com/staart/api/commit/71f81595) Update cosmic to v1.0.1 +- [`517fd0d1`](https://github.com/staart/api/commit/517fd0d1) Update @staart/scripts to v1.18.0 +- [`9bf7fcb5`](https://github.com/staart/api/commit/9bf7fcb5) Update staart/scripts to v1.18.1 +- [`51c8f8e8`](https://github.com/staart/api/commit/51c8f8e8) Update @staart/elasticsearch to v2.2.4 +- [`0edb1ad5`](https://github.com/staart/api/commit/0edb1ad5) Update @staart/scripts to v1.18.2 +- [`114e200d`](https://github.com/staart/api/commit/114e200d) Update @sentry/node, @staart/scripts +- [`34242e21`](https://github.com/staart/api/commit/34242e21) Update update-template to v1.1.2 +- [`1262a0ff`](https://github.com/staart/api/commit/1262a0ff) Update all dependencies +- [`5f48cf67`](https://github.com/staart/api/commit/5f48cf67) Update all dependencies + +### 💥 Breaking changes + +- [`ec11d6f2`](https://github.com/staart/api/commit/ec11d6f2) Add v3 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..336c3415 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM node:14.15.3-alpine3.11 +ENV NODE_ENV production +ENV PORT 80 +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +ENV CHROMIUM_PATH /usr/bin/chromium-browser +RUN apk add --no-cache udev ttf-freefont chromium git g++ gcc libgcc libstdc++ linux-headers make python build-base cairo-dev jpeg-dev pango-dev musl-dev giflib-dev pixman-dev pangomm-dev libjpeg-turbo-dev freetype-dev +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN ["npm", "ci", "--also=dev"] +COPY prisma . +RUN ["npx", "prisma", "generate"] +COPY . . +RUN ["npm", "run", "build"] +EXPOSE 80 +WORKDIR /usr/src/app +CMD ["npm", "run", "start:prod"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..db5b81b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Anand Chowdhary + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..92eec96f --- /dev/null +++ b/README.md @@ -0,0 +1,135 @@ +[![Staart API](https://raw.githubusercontent.com/staart/staart.js.org/master/assets/svg/api.svg?sanitize=true)](https://staart.js.org/api) + +Staart API is a Node.js backend starter for SaaS startups written in TypeScript. It has all the features you need to build a SaaS product, like user management and authentication, billing, organizations, GDPR tools, API keys, rate limiting, superadmin impersonation, and more. + +| | Status | +| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Build | [![Node CI](https://github.com/koj-co/template/workflows/Node%20CI/badge.svg)](https://github.com/koj-co/template/actions?query=workflow%3A%22Node+CI%22) [![Snyk Vulnerabilities for GitHub Repo](https://img.shields.io/snyk/vulnerabilities/github/koj-co/template)](https://snyk.io/test/github/koj-co/template) [![Dependencies](https://img.shields.io/david/staart/api.svg)](https://david-dm.org/staart/api) [![Dev dependencies](https://img.shields.io/david/dev/staart/api.svg)](https://david-dm.org/staart/api) | +| PRs | [![Pull Request Labeler](https://github.com/koj-co/template/workflows/Pull%20Request%20Labeler/badge.svg)](https://github.com/koj-co/template/actions?query=workflow%3A%22Pull+Request+Labeler%22) [![PR Generator CI](https://github.com/koj-co/template/workflows/PR%20Generator%20CI/badge.svg)](https://github.com/koj-co/template/actions?query=workflow%3A%22PR+Generator+CI%22) [![Merge PRs](https://github.com/koj-co/template/workflows/Merge%20PRs/badge.svg)](https://github.com/koj-co/template/actions?query=workflow%3A%22Merge+PRs%22) | +| Community | [![Contributors](https://img.shields.io/github/contributors/staart/api.svg)](https://github.com/staart/api/graphs/contributors) [![GitHub](https://img.shields.io/github/license/staart/api.svg)](https://github.com/staart/api/blob/master/LICENSE) ![Type definitions](https://img.shields.io/badge/types-TypeScript-blue.svg) [![npm package version](https://img.shields.io/npm/v/@staart/api)](https://www.npmjs.com/package/@staart/api) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) | + +Staart API is build to work with [Staart UI](https://github.com/staart/ui), the frontend PWA starter for SaaS startups. + +**⚠️ v3 BETA WARNING:** The `master` branch and all 3.x releases are currently in beta. For production, use v1.x instead. + +## ⭐ Features + +### 🆕 New in v2 + +- Casbin-powered permission management +- JWT-powered single-use coupon codes +- Redis-powered queues for outbound emails and logs +- Cloud agnostic, no longer specific to AWS +- Staart scripts for building and deploying +- Async JSON response and smart controller injection + +### 🔐 Security + +- JWT-powered authentication and user management +- TOTP-powered two-factor authentication (2FA) +- OAuth2 login with third-party accounts +- Location-based login verification +- Security event logging and history + +### 💳 SaaS + +- Stripe-powered recurring billing +- Teams with managed user permissions +- CRUD invoices, methods, transactions, etc. +- Rich HTML transactional emails +- GDPR-compliant data export and delete +- API gateway with API keys and rate limiting +- Domain verification with auto-approve members + +### 👩‍💻 Developer utilities + +- OvernightJS-powered decorators and class syntax +- Injection-proof helpers for querying databases +- Data pagination and CRUD utilities for all tables +- Authorization helpers +- Caching and invalidation for common queries +- User impersonation for super-admin +- Easy redirect rules in YAML +- ElasticSearch-powered server and event logs + +## 🛠 Usage + +1. Use this template or fork this repository +1. Install dependencies with `npm install` +1. Add a `.env` file based on [config.ts](https://github.com/staart/api/blob/master/src/config.ts). +1. Create MariaDB/MySQL tables based on [schema.sql](https://github.com/staart/api/blob/master/schema.sql) +1. Add your controllers in the `./src/controllers` directory +1. Generate your `app.ts` file using `staart controllers` +1. Build with `staart build` and deploy with `staart launch` + +### Updating Staart + +To update your installation of Staart, run the following: + +```bash +staart update api +``` + +If you've used the "Use this template" option on GitHub, you might have to force pull from `staart/api` the first time since the histories wouldn't match. You can use the flag `--allow-unrelated-histories` in this case. + +## 💻 Docs + +- [Getting started](https://staart.js.org/api/getting-started.html) +- [Setting up environment variables](https://staart.js.org/api/setting-up-environment-variables.html) +- [Creating a controller](https://staart.js.org/api/creating-a-controller.html) +- [Updating Staart](https://staart.js.org/api/update.html) +- [Response headers](https://staart.js.org/api/response-headers.html) +- [Throwing errors](https://staart.js.org/api/throwing-errors.html) +- [Authorization](https://staart.js.org/api/authorization.html) +- [API key authentication](https://staart.js.org/api/api-key-authentication.html) +- [Redirects](https://staart.js.org/api/redirects.html) +- [Serving static files](https://staart.js.org/api/serving-static-files.html) + +**[View docs site →](https://staart.js.org/api)** + +**[View TypeDoc →](https://staart-typedoc.netlify.com)** + +**[View API demo →](http://staart.prod.oswaldlabs.com)** + +**[View frontend demo →](https://staart-demo.o15y.com)** + +## 🏗️ Built with Staart + +- [Oswald Labs Platform](https://github.com/OswaldLabsOpenSource/platform-v3) +- [Speakup Developer](https://github.com/speakupnl/staart) +- [**Add your Staart-based project**](https://github.com/staart/api/edit/master/README.md) + +## [🏁 Staart Ecosystem](https://staart.js.org) + +The Staart ecosystem consists of open-source projects to build your SaaS startup, written in TypeScript. + +| Package | | | +| -------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [🛠️ Staart API](https://github.com/staart/api) | Node.js backend with RESTful APIs | [![Build status](https://img.shields.io/circleci/build/github/staart/api)](https://circleci.com/gh/staart/api) [![Docs](https://img.shields.io/endpoint?url=https%3A%2F%2Fstaart.js.org%2Fshield-schema%2Fapi.json)](https://staart.js.org/api) [![npm package version](https://img.shields.io/npm/v/@staart/api)](https://www.npmjs.com/package/@staart/api) | +| [🌐 Staart UI](https://github.com/staart/ui) | Frontend Vue.js Progressive Web App | [![Build status](https://img.shields.io/circleci/build/github/staart/ui)](https://circleci.com/gh/staart/ui) [![Docs](https://img.shields.io/endpoint?url=https%3A%2F%2Fstaart.js.org%2Fshield-schema%2Fui.json)](https://staart.js.org/ui) [![npm package version](https://img.shields.io/npm/v/@staart/ui)](https://www.npmjs.com/package/@staart/ui) | +| [📑 Staart Site](https://github.com/staart/site) | Static site generator for docs/helpdesk | [![Build status](https://img.shields.io/circleci/build/github/staart/site)](https://circleci.com/gh/staart/site) [![Docs](https://img.shields.io/endpoint?url=https%3A%2F%2Fstaart.js.org%2Fshield-schema%2Fsite.json)](https://staart.js.org/site) [![npm package version](https://img.shields.io/npm/v/@staart/site)](https://www.npmjs.com/package/@staart/site) | +| [📱 Staart Native](https://github.com/staart/native) | React Native app for Android and iOS | [![Build status](https://img.shields.io/circleci/build/github/staart/native)](https://circleci.com/gh/staart/native) [![Docs](https://img.shields.io/endpoint?url=https%3A%2F%2Fstaart.js.org%2Fshield-schema%2Fnative.json)](https://staart.js.org/native) [![npm package version](https://img.shields.io/npm/v/@staart/native)](https://www.npmjs.com/package/@staart/native) | +| [🎨 Staart.css](https://github.com/staart/css) | Sass/CSS framework and utilities | [![Build status](https://img.shields.io/circleci/build/github/staart/css)](https://circleci.com/gh/staart/css) [![Docs](https://img.shields.io/endpoint?url=https%3A%2F%2Fstaart.js.org%2Fshield-schema%2Fcss.json)](https://staart.js.org/css) [![npm package version](https://img.shields.io/npm/v/@staart/css)](https://www.npmjs.com/package/@staart/css) | +| [📦 Staart Packages](https://github.com/staart/packages) | Helper functions and utility packages | [![Build status](https://img.shields.io/circleci/build/github/staart/packages)](https://circleci.com/gh/staart/packages) [![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fservices.anandchowdhary.now.sh%2Fapi%2Fgithub-files%3Frepo%3Dstaart%2Fpackages%26path%3Dpackages%26label%3Dstaart%26message%3D%25241%2524%2520package%2524S%2524%26color%3Dblueviolet)](https://www.npmjs.com/org/staart) | + +## 💝 Sponsors + +The development of Staart projects is supported by these wonderful companies. [Find us on OpenCollective](https://opencollective.com/staart) + + + + + + + + + + +

Oswald Labs

O15Y

Speakup

Netlify

Koj
+ +## 📄 License + +- Code: [MIT](https://github.com/staart/api/blob/master/LICENSE) +- Logo and assets: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) +- [GeoLite2](https://dev.maxmind.com/geoip/geoip2/geolite2/): [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) +- [GeoNames](http://www.geonames.org/): [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/) diff --git a/docs/authentication.md b/docs/authentication.md new file mode 100644 index 00000000..d496a394 --- /dev/null +++ b/docs/authentication.md @@ -0,0 +1,50 @@ +# Authentication + +Users can log in to Staart API using several methods: + +- Login with an email/password combination +- Passwordless login (only enter your email and receive a login link) +- Custom SAML-based login (coming soon) + +All of these methods also support multi-factor authentication (MFA) using: + +- TOTP-based application +- SMS message +- Email + +On successful login, users receive an access token and a refresh token. The access token is a JSON Web Token (JWT) valid for one hour, and the refresh token is a UUID stored in the `sessions` table in the database with no expiry. Users can manually invalidate the refresh token by deleting the corresponding session, and sessions are auto-deleted after 30 days of inactivity. Each access token includes the scopes a user has access to. Users can also alternately create API keys with scopes for their account or any groups they are a member of. + +Bearer authentication is used to specify the access token or API key, using the `Authorization` header. + +## Routes + +### Public routes + +By default, all endpoints required authentication. The `@Public()` decorator can be used on a controller or specific route to skip authentication. By default, all auth routes (for example, logging in and registration) don't require authentication: + +```ts +@Controller('auth') +@Public() +export class AuthController { + constructor() {} +} +``` + +You can also make a single route public: + +```ts +@Controller('example') +export class ExampleController { + constructor() {} + + @Get('public') + @Public() // The route /example/public is public + async example(): Promise { + return true; + } +} +``` + +#### Under the hood + +The `Public()` decorator was proposed in [nestjs/nest/#5598](https://github.com/nestjs/nest/issues/5598) as a solution to the lack of reorderability of Guards in Nest. diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 00000000..3e4a5806 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,111 @@ +# Configuration + +You can use environment variables to set configuration values. + +## Security + +| Environment variable | Description | Default value | +| --------------------------- | ------------------------------- | ------------- | +| `SALT_ROUNDS` | Number of password salt rounds | 10 | +| `JWT_SECRET` | Secret to sign JWTs | staart | +| `TOTP_WINDOW_PAST` | Expired TOTP tokens to accept | 1 | +| `TOTP_WINDOW_FUTURE` | Future TOTP tokens to accept | 0 | +| `MFA_TOKEN_EXPIRY` | Expiry time for MFA tokens | 10m | +| `MERGE_USERS_TOKEN_EXPIRY` | Expiry time to merging link | 30m | +| `ACCESS_TOKEN_EXPIRY` | Expiry time for access tokens | 1h | +| `PASSWORD_PWNED_CHECK` | Check for Pwned passwords | false | +| `DELETE_EXPIRED_SESSIONS` | Delete inactive sessions (days) | 30 | +| `INACTIVE_USER_DELETE_DAYS` | Delete deactivated users (days) | 30 | + +## Email + +You can set the following environment variables to specify the name and from email address: + +| Environment variable | Description | +| -------------------- | ------------------- | +| `EMAIL_NAME` | Name of the service | +| `EMAIL_FROM` | From email address | + +If you want to use SMTP, you should additionally set the configuration: + +| Environment variable | Description | +| -------------------- | ---------------- | +| `EMAIL_HOST` | Host | +| `EMAIL_PORT` | Port | +| `EMAIL_SECURE` | Secure (boolean) | +| `EMAIL_USER` | Username | +| `EMAIL_PASSWORD` | Password | + +Alternately, if you want to use AWS SES, you should set these instead (note that you can also use SMTP with SES): + +| Environment variable | Description | +| ----------------------------- | -------------- | +| `EMAIL_SES_ACCESS_KEY_ID` | AWS access key | +| `EMAIL_SES_SECRET_ACCESS_KEY` | AWS secret key | +| `EMAIL_SES_REGION` | AWS region | + +To generate an access/secret key pair, you can create an IAM user with the permission `AmazonSESFullAccess`. For more details, read the article [Creating an IAM user in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) on the AWS website. + +### Rate limiting + +Staart API has three types of rate limits. When an endpoint is accessed, 1 point is consumed. There are also some endpoints that consume additional points (like logging in or creating an account consumes 10 points). The types of rate limits are: + +1. "Public" for unauthenticated requests (250 points/hour) +2. "Authenticated" for requests with a user access token (5k points/hour) +3. "API key" for (automated) requests using an API key (10k points/hour) + +You can set the rate limits for each of these categories. By default, the rate limit resets after one hour: + +| Environment variable | Description | Default | +| ----------------------------------- | -------------------------------- | ------- | +| `RATE_LIMIT_PUBLIC_POINTS` | Maximum points for public | 250 | +| `RATE_LIMIT_PUBLIC_DURATION` | Reset duration for public | 3600 | +| `RATE_LIMIT_AUTHENTICATED_POINTS` | Maximum points for authenticated | 5000 | +| `RATE_LIMIT_AUTHENTICATED_DURATION` | Reset duration for authenticated | 3600 | +| `RATE_LIMIT_API_KEY_POINTS` | Maximum points for API key | 10000 | +| `RATE_LIMIT_API_KEY_DURATION` | Reset duration for API key | 3600 | + +## Optional services + +### ElasticSearch + +ElasticSearch is used for tracking API key logs. + +If you have a public ElasticSearch instance (this is not recommended), you only need to specify the node: + +```env +ELASTICSEARCH_NODE = "https://your-endpoint.example" +``` + +If your endpoint uses HTTP basic authentication, you can add the credentials: + +```env +ELASTICSEARCH_AUTH_USERNAME = "Your username" +ELASTICSEARCH_AUTH_PASSWORD = "Your password" +``` + +Or, if you're using an ElasticSearch-hosted instance with an API key, you can provide only the API key or a combination of the API key and ID: + +```env +ELASTICSEARCH_AUTH_API_KEY = "Your API key" +ELASTICSEARCH_AUTH_API_KEY_ID = "Your API key ID" +``` + +Alternately, if you're using the Amazon Elasticsearch Service, you can specify the AWS credentials: + +```env +ELASTICSEARCH_NODE = "https://search-your-endpoint.us-east-1.es.amazonaws.com" +ELASTICSEARCH_AWS_ACCESS_KEY_ID = "Your AWS access key ID" +ELASTICSEARCH_AWS_SECRET_ACCESS_KEY = "Your AWS secret access key" +ELASTICSEARCH_AWS_REGION = "us-east-1" +``` + +### Twilio SMS + +To send SMS messages using Twilio, you should set the following environment variables: + +| Environment variable | Description | +| -------------------- | --------------------- | +| `TWILIO_ACCOUNT_SID` | Twilio account SID | +| `TWILIO_AUTH_TOKEN` | Twilio auth token | +| `SMS_FAIL_RETRIES` | Number of SMS retries | diff --git a/docs/database.md b/docs/database.md new file mode 100644 index 00000000..2316a3fe --- /dev/null +++ b/docs/database.md @@ -0,0 +1,41 @@ +# Database + +Staart uses [prisma/prisma](https://github.com/prisma/prisma) under the hood, which is a modern ORM alternative. You can ues any of the databases supported by Prisma: PostgreSQL, MySQL, or SQLite. Support for Microsoft SQL Server is also in beta. Your database should already be created before setting up the connection. + +## Database connection + +To get started, add the required required environment variables in your `.env` file. The `DATABASE_URL` variable is usually set with [dotenv-expand](https://github.com/motdotla/dotenv-expand) variables: + +```env title=".env" +DATABASE_URL = "${DB_PROVIDER}://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_POST}/${DB_NAME}" +``` + +Then, you can set the individual environment variables: + +| Environment variable | Description | +| -------------------- | -------------------------------- | +| `DB_PROVIDER` | Database provider, e.g., "mysql" | +| `DB_USER` | Username | +| `DB_PASSWORD` | Password | +| `DB_HOST` | Hostname | +| `DB_PORT` | Database port, e.g., 3306 | +| `DB_NAME` | Databse name to use | + +Alternately, you can also set the `DATABASE_URL` variable directly. For more information about this connection, you can visit the article on the Prisma website: [Using environment variables](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema#using-environment-variables). + +## Setting up + +When you're setting your database for the first time, you should use the `prisma db push` command: + +``` +npx prisma db push --preview-feature +``` + +When you make changes to your database schema, you should use the `prisma migrate` command: + +``` +npx prisma migrate save --experimental +npx prisma migrate up --experimental +``` + +You can learn more about the `migrate` command on the documentation page: [Prisma Migrate](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate). diff --git a/docs/email.md b/docs/email.md new file mode 100644 index 00000000..20d2dcd8 --- /dev/null +++ b/docs/email.md @@ -0,0 +1,33 @@ +# Email + +Staart API supports sending transactions emails. You can send emails using any SMTP provider or [nodemailer/nodemailer](https://github.com/nodemailer/nodemailer)-supported configuration, such as AWS SES or `sendmail`. + +## Usage + +### Templates + +To send a new email, you can use the `MailService`: + +```ts +class ExampleService { + constructor(private mailService: MailService) {} + + doSomething() { + return this.mailService.send({ + to: 'Example User ', + template: 'auth/password-reset', + data: { + name: 'Example User', + }, + }); + } +} +``` + +In the above example, you are sending an email using the password reset template available at `src/templates/auth/password-reset.md`. Templates are written in Markdown and injected with variables using Mustache, and you can create as many as you like. + +Note that the `send()` function is synchronous and adds your message to a queue (you do not neet to use `await`), and all messages are sequentially sent. Failed messages are retried, up to 3 times (you can configure this using the environment variable `EMAIL_FAIL_RETRIES`). + +### Without templates + +You can also directly send emails by specifying the `subject` and multiple formats, `html` and `text`. diff --git a/docs/environment-variables.md b/docs/environment-variables.md new file mode 100644 index 00000000..97eb6caa --- /dev/null +++ b/docs/environment-variables.md @@ -0,0 +1,154 @@ +# Eenvironment variables + +All environment variables are stored in a `.env` file locally. All environment variables are optional, with the exception of the database URL. + +## Port + +The first and most important is the port you want the Staart backend process to run on. On production, this is usually `80`. Locally, it can be something like `3000` or `8080`. In these examples, we'll use the port 7007. + +```env +PORT = 7007 +``` + +This will launch the app on http://localhost:7007. + +### URLs + +When sending emails and performing redirects, the URL of your frontend app is required. In most cases, this is the URL on which [Staart UI](https://github.com/staart/ui) is running: + +```env +FRONTEND_URL = "https://example.com" +``` + +### Database connection + +Once you have your database details (host, username, password, and databse name), you can create a file with the path `prisma/.env` with your database URL: + +```env +DATABASE_URL = "postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA" +``` + +Similarly, a MySQL or MariaDB URL looks like: + +```env +DATABASE_URL = "mysql://USER:PASSWORD@HOST:PORT/DATABSE" +``` + +You can read more about this URL on the [Add to existing project](https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project-typescript-postgres#connect-your-database) article on the Prisma docs website. + +### Sending emails + +Staart API sends transactions emails for email verification, password resets, etc. You can either send emails using SMTP or AWS SES. If you want to use SMTP: + +```env +EMAIL_HOST = "smtp.example.com" +EMAIL_FROM = "hello@example.com" +EMAIL_PASSWORD = "your-password" +``` + +Alternately, you can use AWS SES to send emails: + +```env +SES_EMAIL = "hello@example.com" +SES_REGION = "eu-west-2" +SES_ACCESS = "aws-access-key-xxxxxxxxxx" +SES_SECRET = "aws-secret-key-xxxxxxxxxx" +``` + +In the above example, emails are sent from `hello@example.com` and SES is setup in the `eu-west-2` AWS region with the given credentials. + +### Billing + +We use Stripe for billing and subscription management. You'll need your Stripe secret key and a product ID (you can use your test key to make sure people aren't actually charged). We use the product ID to show the pricing plans of that product. + +```env +STRIPE_SECRET_KEY = "stripe-test-api-key" +STRIPE_PRODUCT_ID = "stripe-product-id" +``` + +### Encryption keys + +It is highly recommended that you change the default encryption keys for signing JWTs, salts for hashes, etc. These should just be large (10+ character) strings, preferably a mix of letters, numbers, and special characters: + +```env +JWT_SECRET = "secret" +TWT_SECRET = "secret" +``` + +### Redis + +Staart API uses both Redis-based and in-memory caches to optimize queries and requests. If you’re running Redis using the default settings, you can ignore the `REDIS_URL` key, or populate it with your instance’s URL if it’s managed or on another port: + +The TTL determines how long a result is cached in the memory; by default, this is 10 minutes. The check period tells Staart how often to check the cache and remove expired items (think of it like a `setInterval`). Organization details, API key scopes, etc., are stored in-memory for faster access, and Redis is primarily used to invalidate JWTs. + +```env +REDIS_URL = "redis://127.0.0.1:6379" +CACHE_TTL = 600 # 10 mins +CACHE_CHECK_PERIOD = 1000 # 1,000 s +``` + +### Rate limits + +Rate limits help Staart mitigate some types of attacks. By default, users can do 60 requests/minute without an API key. With API key authentication, this is increased to 1,000 requests/minute. There is also a speed limit mechanism that delays requests by 100ms (in response time) per request if more than 500 are made in a minute. + +Brute force prevention is used on authentication endpoints, where only 50 requests per 5 minutes are allowed, and users have to wait for (an increasing amount of) some time before they can make another request. + +```env +## Brute force is used for auth endpoints +BRUTE_FREE_RETRIES = 50 # 50 requests +BRUTE_LIFETIME = 300000 # in 5 mins + +## Public limits +PUBLIC_RATE_LIMIT_MAX = 60 # 60 requests +PUBLIC_RATE_LIMIT_TIME = 60000 # in 1 min +SPEED_LIMIT_COUNT = 500 # 1k requests +SPEED_LIMIT_TIME = 600000 # in 1 min +SPEED_LIMIT_DELAY = 100 # delay 100ms + +## Limits when using an API key +RATE_LIMIT_MAX = 1000 # 1k requests +RATE_LIMIT_TIME = 60000 # in 1 min +``` + +### Expiry durations + +JWTs are used for authentication and emails, and you can configure their expiry duration. Email verification links are valid for one week, password reset links for a day, and location approval links for 10 minutes. + +In terms of authentication, a login session token is approved for 15 minutes and a refresh token is valid for 1 month. + +```env +TOKEN_EXPIRY_EMAIL_VERIFICATION = "7d" +TOKEN_EXPIRY_PASSWORD_RESET = "1d" +TOKEN_EXPIRY_LOGIN = "15m" +TOKEN_EXPIRY_APPROVE_LOCATION = "10m" +TOKEN_EXPIRY_REFRESH = "30d" +``` + +### Disallow disposable emails + +If you don't want users to be able to sign up using a disposable email address, set this to `false`. + +```env +ALLOW_DISPOSABLE_EMAILS = false +``` + +### ElasticSearch for data and tracking + +We use ElasticSearch to track events, analytics, and server logs. You can use AWS-managed ElasticSearch or your own instance. If you use AWS: + +```env +AWS_ELASTIC_HOST = "https://name.region.es.amazonaws.com" +AWS_ACCESS_KEY_ID = "aws-access-key-xxxxxxxxxx" +AWS_SECRET_ACCESS_KEY = "aws-secret-key-xxxxxxxxxx" +AWS_REGION = "eu-west-2" +``` + +Alternately, you can also use a custom ElasticSearch service if you don’t want to use AWS. In this case, you have to specify the host, log, and API version. + +### Sentry error tracking + +If you want to track errors using Sentry, you can supply your DSN: + +```env +SENTRY_DSN = "https://@sentry.io/" +``` diff --git a/docs/get-started.md b/docs/get-started.md new file mode 100644 index 00000000..c4b41b9d --- /dev/null +++ b/docs/get-started.md @@ -0,0 +1,84 @@ +# Getting started + +**Staart API** helps you build your software-as-a-service (SaaS) backend without the hassles. It includes features that every SaaS product needs, like authentication, recurring billing, teams, emails, and more. + +To get started, you can use GitHub's template feature (see [Creating a repository from a template](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template)) by going to the [staart/api](https://github.com/staart/api) repository and clicking the "Use this template" button. + +## After creating the repository + +### Clone + +Once you've created the template repository, you can clone it and enter the project directory. In the following example, "repo" is the name of the repository and "username" is your GitHub username. + +```bash +git clone https://github.com/username/repo && cd repo +``` + +### Install dependencies + +Install dependencies using npm: + +```bash +npm install +``` + +### Change package.json + +You can add your repository metadata in the `package.json` file. + +```json +{ + "name": "your-project-name", + "version": "0.0.0" +} +``` + +You can change `@staart/api` with `your-project-name`. It is also recommended to change the version to `0.0.0` so that Semantic Release can take over the versioning process on push. + +Optionally, you can update the `repository`, `author`, and `license` keys based on your preferences. + +### Set up your database + +Initially, we'll start by migrating the Staart database schema to your database. Later on, you can update the schema to your liking. + +First, create a remote or local database. You can use any SQL-based database management system supported by Prisma (see [Supported databases](https://www.prisma.io/docs/more/supported-databases)), such as MySQL, MariaDB, PostgreSQL, or SQLite. AWS-managed databases such as Aurora and Aurora Serverless are also supported. + +Once you have your database details (host, username, password, and databse name), you can create a file with the path `prisma/.env` with your database URL: + +```env +DATABASE_URL = "postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA" +``` + +Note that this environment variable file is in the `prisma` directory, and is separate from the file in the project root. You can read more about this URL on the [Add to existing project](https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project-typescript-postgres#connect-your-database) article on the Prisma docs website. + +#### Setting up your database + +Once you've added the database URL, you can perform the following step to create SQL tables (see [Prisma Migrate](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate)) + +```bash +npx prisma db push --preview-feature +``` + +### Add environment variables + +The next step is to add the basic environment variables. Start by creating a `.env` file in the project directory and add the `APP_NAME`. You should also add the `DATABASE_URL` that you previously added to `prisma/.env`: + +```env +APP_NAME = "Staart" +DATABASE_URL = "mysql://root:password@example.com:3306/database-name" +``` + +### Run locally + +To run the project, use the following npm script: + +```bash +npm run start:dev +``` + +Alternately, you can use separate `build` and `launch` steps, for example in a Dockerfile: + +```bash +npm run build +npm run launch +``` diff --git a/docs/scopes.md b/docs/scopes.md new file mode 100644 index 00000000..d4d1dad2 --- /dev/null +++ b/docs/scopes.md @@ -0,0 +1,64 @@ +# Authorization with scopes + +The `@Scopes()` decorator is used on routes to ensure scope-based authorization. To better understand how Staart works with scopes in routes, let's look at the controller for a user's emails: + +```ts +@Controller('users/:userId/emails') +export class EmailController { + constructor(private emailsService: EmailsService) {} + @Get(':id') + @Scopes('user-{userId}:read-email-{id}') + async get( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.emailsService.getEmail(userId, Number(id)); + } +} +``` + +In line 5 of the above code snippet, the scope for this endpoints is `@Scopes('user-{userId}:read-email-{id}')`. The route parameters `userId` and `id` will be replaced with their values. For example, for a user with ID 123 to get an email with ID 456, the scope required is `user-123:read-email-456`. + +## Glob matching + +The scope provided to a user (with ID 123, for example) when logging in to their account is `user-123:*`. Staart uses glob-based matching to ensure their have access to authorized routes. + +In the above example, the user can access this resource because `user-123:*` matches `user-123:read-email-456`. + +When creating personal access tokens using the interface or API, users can control which scopes they want access to. The above example scope `user-123:read-email-456` will be successfully matched by any of these values: + +| Scope | Access to | +| ------------------------- | --------------------------------------------- | +| `user-123:read-email-456` | Read only this particular email | +| `user-123:read-email-*` | Read all emails for this user | +| `user-123:read-*` | Read all resources for this user | +| `user-123:*` | Read/write/delete all resources for this user | + +There are also superuser scopes that are not available as personal access token scopes, but can be manually granted. These will, for example, also match this scope: + +| Scope | Access to | +| --------------------- | --------------------------------------------- | +| `user-*:read-email-*` | Read all emails for all users | +| `user-*:read-*` | Read all resources for all users | +| `user-*:*` | Read/write/delete all resources for all users | +| `*` | Read/write/delete all resources | + +## `@Scopes()` decorator + +The decorator can be used to which scopes a route requires. + +Like mentioned above, the scopes decorator automatically replaces any route params in curly braces (`{}`): + +```ts +@Scopes('user-{userId}:read-email-{id}') // user-123:read-email-456 +``` + +Multiple scopes can also be specified, and permission will be granted if at least one of these scopes are matched: + +```ts +@Scopes('user-{userId}:read-email-{id}', 'another-scope') +``` + +## Under the hood + +The [`scope.guard.ts`](/src/modules/auth/scope.guard.ts) file contains the source code for this Guard. The matching utility [minimatch](https://github.com/isaacs/minimatch) is used to compare scopes, which in turn works by converting glob expressions into regular expressions. diff --git a/docs/tests.md b/docs/tests.md new file mode 100644 index 00000000..49341bb7 --- /dev/null +++ b/docs/tests.md @@ -0,0 +1,72 @@ +# Tests + +This API has both unit tests and end-to-end tests. + +## Unit tests + +Currently, unit tests are focused on single services in providers. In the near future, unit tests should be added to each module by mocking their services and testing their controllers. + +Unit tests use [facebook/jest](https://github.com/facebook/jest). To run unit tests: + +```bash +npm run test:unit +``` + +
+ Expected output + +``` +> jest --forceExit + + PASS src/providers/dns/dns.service.spec.ts (14.789 s) + PASS src/providers/tokens/tokens.service.spec.ts (14.868 s) + PASS src/providers/geolocation/geolocation.service.spec.ts (14.951 s) + PASS src/providers/pwned/pwned.service.spec.ts (15.241 s) + +Test Suites: 4 passed, 4 total +Tests: 17 passed, 17 total +Snapshots: 0 total +Time: 16.382 s +Ran all test suites. +``` + +
+ +## End-to-end tests + +End-to-end tests use [visionmedia/supertest](https://github.com/visionmedia/supertest) for requests and [facebook/jest](https://github.com/facebook/jest) for assertions. + +To run end-to-end tests: + +```bash +npm run test:e2e +``` + +
+ Expected output + +``` +> export TEST=true; npx ts-node tests/test-before.ts && jest --runInBand --config ./tests/jest-e2e.json --forceExit + + AppController (e2e) + ✓ gets / (49 ms) + ✓ registers and logs in (5008 ms) + ✓ gets user details (4231 ms) + ✓ creates and uses API key (3424 ms) +``` + +
+ +The goal of end-to-end tests is not to test the behavior of each API endpoint, but to ensure the overall behavior of the system works. For this API, we perform the following steps: + +1. Visit `/` and ensure it redirects to the GitHub project +2. Create a new user account and log in from that account +3. Get `/user/{id}` for the new account using the access token +4. Create an API key for the user with the `read-info` scope +5. Get `/user/{id}` for the new account using the API key + +Here, (1) ensures that the app has launched successfully and is responding correctly to API requests. It also tests the `@Public()` decorator and shows that API endpoints may be available without any authentication requirements. (2) ensures that the database connection is working (new user added) and authentication endpoints are working. (3) and (4) show that authenticated routes work, and (5) tests that API key authentication works with scopes. + +## See also + +- [NestJS testing](https://docs.nestjs.com/fundamentals/testing) diff --git a/heroku.yml b/heroku.yml new file mode 100644 index 00000000..8eec25b9 --- /dev/null +++ b/heroku.yml @@ -0,0 +1,3 @@ +build: + docker: + web: Dockerfile diff --git a/http/create-access-token.http b/http/create-access-token.http new file mode 100644 index 00000000..73bac4c0 --- /dev/null +++ b/http/create-access-token.http @@ -0,0 +1,10 @@ +@baseUrl = http://localhost:3000/v1 +@userId = 1 + +POST {{baseUrl}}/users/{{userId}}/access-tokens +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} + +{ + "scopes": ["user-1:read-info"] +} diff --git a/http/create-api-key.http b/http/create-api-key.http new file mode 100644 index 00000000..2567e249 --- /dev/null +++ b/http/create-api-key.http @@ -0,0 +1,10 @@ +@baseUrl = http://localhost:3000/v1 +@groupId = 1 + +POST {{baseUrl}}/groups/{{groupId}}/api-keys +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} + +{ + "scopes": ["group-1:*"] +} diff --git a/http/create-group.http b/http/create-group.http new file mode 100644 index 00000000..9c95ab5b --- /dev/null +++ b/http/create-group.http @@ -0,0 +1,10 @@ +@baseUrl = http://localhost:3000/v1 +@userId = 1 + +POST {{baseUrl}}/users/{{userId}}/memberships +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} + +{ + "name": "Koj" +} diff --git a/http/forgot-password.http b/http/forgot-password.http new file mode 100644 index 00000000..b446f81c --- /dev/null +++ b/http/forgot-password.http @@ -0,0 +1,8 @@ +@baseUrl = http://localhost:3000/v1 + +POST {{baseUrl}}/auth/forgot-password +content-type: application/json + +{ + "email": "staart@anandchowdhary.com" +} diff --git a/http/get-access-token-scopes.http b/http/get-access-token-scopes.http new file mode 100644 index 00000000..9d617d7d --- /dev/null +++ b/http/get-access-token-scopes.http @@ -0,0 +1,6 @@ +@baseUrl = http://localhost:3000/v1 +@userId = 1 + +GET {{baseUrl}}/users/{{userId}}/access-tokens/scopes +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} diff --git a/http/get-access-tokens.http b/http/get-access-tokens.http new file mode 100644 index 00000000..b1eae9e0 --- /dev/null +++ b/http/get-access-tokens.http @@ -0,0 +1,6 @@ +@baseUrl = http://localhost:3000/v1 +@userId = 1 + +GET {{baseUrl}}/users/{{userId}}/access-tokens +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} diff --git a/http/get-group-with-api-key.http b/http/get-group-with-api-key.http new file mode 100644 index 00000000..03262879 --- /dev/null +++ b/http/get-group-with-api-key.http @@ -0,0 +1,6 @@ +@baseUrl = http://localhost:3000/v1 +@groupId = 1 + +GET {{baseUrl}}/groups/{{groupId}} +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_API_KEY}} diff --git a/http/get-group.http b/http/get-group.http new file mode 100644 index 00000000..6dde193b --- /dev/null +++ b/http/get-group.http @@ -0,0 +1,6 @@ +@baseUrl = http://localhost:3000/v1 +@groupId = 1 + +GET {{baseUrl}}/groups/{{groupId}} +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} diff --git a/http/get-user-with-access-token.http b/http/get-user-with-access-token.http new file mode 100644 index 00000000..287e12f2 --- /dev/null +++ b/http/get-user-with-access-token.http @@ -0,0 +1,6 @@ +@baseUrl = http://localhost:3000/v1 +@userId = 1 + +GET {{baseUrl}}/users/{{userId}} +content-type: application/json +X-Access-Token: {{$dotenv HTTP_TEST_USER_ACCESS_TOKEN}} diff --git a/http/get-user.http b/http/get-user.http new file mode 100644 index 00000000..a4e5d31a --- /dev/null +++ b/http/get-user.http @@ -0,0 +1,6 @@ +@baseUrl = http://localhost:3000/v1 +@userId = 1 + +GET {{baseUrl}}/users/{{userId}} +content-type: application/json +Authorization: Bearer {{$dotenv HTTP_TEST_ACCESS_TOKEN}} diff --git a/http/login.http b/http/login.http new file mode 100644 index 00000000..ab9c9936 --- /dev/null +++ b/http/login.http @@ -0,0 +1,9 @@ +@baseUrl = http://localhost:3000/v1 + +POST {{baseUrl}}/auth/login +content-type: application/json + +{ + "email": "staart@anandchowdhary.com", + "password": "{{$dotenv HTTP_TEST_PASSWORD}}" +} diff --git a/http/refresh.http b/http/refresh.http new file mode 100644 index 00000000..83d5d8a3 --- /dev/null +++ b/http/refresh.http @@ -0,0 +1,8 @@ +@baseUrl = http://localhost:3000/v1 + +POST {{baseUrl}}/auth/refresh +content-type: application/json + +{ + "token": "{{$dotenv HTTP_TEST_REFRESH_TOKEN}}" +} diff --git a/http/register.http b/http/register.http new file mode 100644 index 00000000..208526fa --- /dev/null +++ b/http/register.http @@ -0,0 +1,10 @@ +@baseUrl = http://localhost:3000/v1 + +POST {{baseUrl}}/auth/register +content-type: application/json + +{ + "name": "Anand Chowdhary", + "email": "staart@anandchowdhary.com", + "password": "{{$dotenv HTTP_TEST_PASSWORD}}" +} diff --git a/http/reset-password.http b/http/reset-password.http new file mode 100644 index 00000000..be8dd85c --- /dev/null +++ b/http/reset-password.http @@ -0,0 +1,9 @@ +@baseUrl = http://localhost:3000/v1 + +POST {{baseUrl}}/auth/reset-password +content-type: application/json + +{ + "token": "{{$dotenv PASSWORD_RESET_TOKEN}}", + "password": "{{$dotenv HTTP_TEST_PASSWORD}}" +} diff --git a/http/verify-email.http b/http/verify-email.http new file mode 100644 index 00000000..bd008a97 --- /dev/null +++ b/http/verify-email.http @@ -0,0 +1,8 @@ +@baseUrl = http://localhost:3000/v1 + +POST {{baseUrl}}/auth/verify-email +content-type: application/json + +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjA0NDA1NTc5LCJleHAiOjE2MDUwMTAzNzksInN1YiI6IkVNQUlMX1ZFUklGWV9UT0tFTiJ9.XHKCmXt142YH-rDnUFrzcvxGfHWA6njVfixZ-cQuoSo" +} diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..a93551fc --- /dev/null +++ b/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", +}; diff --git a/nest-cli.json b/nest-cli.json new file mode 100644 index 00000000..2e1000f0 --- /dev/null +++ b/nest-cli.json @@ -0,0 +1,7 @@ +{ + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "plugins": ["@nestjs/swagger/plugin"] + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..5bbfc0c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,19107 @@ +{ + "name": "@staart/api", + "version": "4.0.9", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/core": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.1.0.tgz", + "integrity": "sha512-O2oIcqpQKGvYJH88d/NCgLYZGc9laA1eo2d1s0FH1Udu4c2L+bAsviQqtTKNmzyaqODHrlkt+eKx7uakdwWtnQ==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "@angular-devkit/schematics": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.1.0.tgz", + "integrity": "sha512-6qfR5w1jyk8MC+5Tfimz+Czsq3WlsVoB57dpxSZfhGGsv1Vxc8Q41y5f3BrAyEqHYjcH7NtaoLQoJjtra5KaAg==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.1.0", + "ora": "5.2.0", + "rxjs": "6.6.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ora": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.2.0.tgz", + "integrity": "sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@angular-devkit/schematics-cli": { + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-0.1102.0.tgz", + "integrity": "sha512-BkIoRf7L+3al+QIYWrgUlpNQP01tPSynPdTNb7z13tCHVv8Ag1CR5SJn1O8IGfFz7NxfX0Me0s7zyNTZ1QaWQQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "@angular-devkit/schematics": "11.2.0", + "@schematics/schematics": "0.1102.0", + "ansi-colors": "4.1.1", + "inquirer": "7.3.3", + "minimist": "1.2.5", + "symbol-observable": "3.0.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.2.0.tgz", + "integrity": "sha512-sMDacACJbA4pykiqgJf/RdW0damcf4mDqErGgEqs/bGG+SBUb8+wgt4cQnUwwVX5V2nMdvv7f0A84rgR6I3G2w==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "ora": "5.3.0", + "rxjs": "6.6.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@elastic/elasticsearch": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-7.11.0.tgz", + "integrity": "sha512-AFVVuANIdbV1qYjuOi4hnsX/DehWYG+bbhQO4amq9K4/NnzU7mpGWOPgVlRQTiX+vBfBkx7SL6h4QEjIlM3ztA==", + "requires": { + "debug": "^4.1.1", + "hpagent": "^0.1.1", + "ms": "^2.1.1", + "pump": "^3.0.0", + "secure-json-parse": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "@eslint/eslintrc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@googlemaps/google-maps-services-js": { + "version": "3.1.16", + "resolved": "https://registry.npmjs.org/@googlemaps/google-maps-services-js/-/google-maps-services-js-3.1.16.tgz", + "integrity": "sha512-Hb1zItxsm68vkWb5i+/wDj64ZK66DZlatglkdNWAWM2ziARdgj+Q4XeWxbkdHh+/OKmeKEcknC8ADF9ZgS/G1w==", + "requires": { + "agentkeepalive": "^4.1.0", + "axios": "^0.21.1", + "query-string": "^6.11.0", + "retry-axios": "^2.2.1" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@koj/config": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@koj/config/-/config-1.2.11.tgz", + "integrity": "sha512-SD0jEUQToEe8cYz4dp9YScGaErHdK89BVUzi/fwb0o3ivkQw25m/9Ks8ogBQWYhS0WGnQWvoaFNTdMYuQyMVvg==", + "requires": { + "@semantic-release/changelog": "^5.0.1", + "@semantic-release/git": "^9.0.0", + "@semantic-release/github": "^7.1.1", + "@semantic-release/npm": "^7.0.6", + "dateformat": "^3.0.3", + "semantic-release": "^17.1.1", + "semantic-release-gitmoji": "^1.3.4" + } + }, + "@nestjs/cli": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-7.5.5.tgz", + "integrity": "sha512-j45givEQQxMwZA4z78XQyYwNrLoe3PHtTRZsneRIWmgSNJ6t0Lfv9MGbNTot0hQ6LYLiEPnvWTpgg2tkKrwUmA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "@angular-devkit/schematics": "11.2.0", + "@angular-devkit/schematics-cli": "0.1102.0", + "@nestjs/schematics": "^7.1.0", + "@types/webpack": "4.41.26", + "chalk": "3.0.0", + "chokidar": "3.5.1", + "cli-table3": "0.5.1", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "6.1.0", + "inquirer": "7.3.3", + "node-emoji": "1.10.0", + "ora": "5.3.0", + "os-name": "4.0.0", + "rimraf": "3.0.2", + "shelljs": "0.8.4", + "tree-kill": "1.2.2", + "tsconfig-paths": "3.9.0", + "tsconfig-paths-webpack-plugin": "3.3.0", + "typescript": "4.1.5", + "webpack": "5.11.0", + "webpack-node-externals": "2.5.2" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.2.0.tgz", + "integrity": "sha512-sMDacACJbA4pykiqgJf/RdW0damcf4mDqErGgEqs/bGG+SBUb8+wgt4cQnUwwVX5V2nMdvv7f0A84rgR6I3G2w==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "ora": "5.3.0", + "rxjs": "6.6.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@nestjs/common": { + "version": "7.6.11", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-7.6.11.tgz", + "integrity": "sha512-DPBqoORDJC9xO/c83ZXNdUk9rIOLZ1Joqt8hYVtGROMIt7+s4zdbiUOWbNLPhUwluIVczYt4bFjhoU6HWoePgA==", + "requires": { + "axios": "0.21.1", + "iterare": "1.2.1", + "tslib": "2.1.0", + "uuid": "8.3.2" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, + "@nestjs/config": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-0.6.3.tgz", + "integrity": "sha512-JxvvUpmH0/WOrTB+zh8dEkxSUQXhB7V3d/qeQXyCnMiEFjaq89+fNFztpWjz4DlOfdS4/eYTzIEy9PH2uGnfzA==", + "requires": { + "dotenv": "8.2.0", + "dotenv-expand": "5.1.0", + "lodash.get": "4.4.2", + "lodash.has": "4.5.2", + "lodash.set": "4.3.2", + "uuid": "8.3.2" + } + }, + "@nestjs/core": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-7.6.13.tgz", + "integrity": "sha512-8oY8yZSgri2DngqmvBMtwYw1GIAaXbUXS7Y0mp/iSZ6jP7CQqYCybdcMPneunrt5PG8rtJsq6n+4JNRvxXrVmA==", + "requires": { + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.0.7", + "iterare": "1.2.1", + "object-hash": "2.1.1", + "path-to-regexp": "3.2.0", + "tslib": "2.1.0", + "uuid": "8.3.2" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, + "@nestjs/mapped-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-0.3.0.tgz", + "integrity": "sha512-AdWVTOg3AhAEcVhPGgUJiLbLXb7L5Pe7vc20YQ0oOXP/KD/nJj0I3BcytVdBhzmgepol67BdivNUvo27Hx3Ndw==" + }, + "@nestjs/passport": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-7.1.5.tgz", + "integrity": "sha512-Hu9hPxTdBZA0C4GrWTsSflzwsJ99oAk9jqAwpcszdFNqfjMjkPGuCM9QsVZbBP2bE8fxrVrPsNOILS6puY8e/A==" + }, + "@nestjs/platform-express": { + "version": "7.6.12", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-7.6.12.tgz", + "integrity": "sha512-tnWCr5V/rs+DUjlNq532iX+p3ACnoTPODCqlxiR7w5wjx0ENy3zFoKj9nuDpVUALgAs5jMrp3/Gdw3PT1V5RHw==", + "requires": { + "body-parser": "1.19.0", + "cors": "2.8.5", + "express": "4.17.1", + "multer": "1.4.2", + "tslib": "2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, + "@nestjs/schedule": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-0.4.2.tgz", + "integrity": "sha512-TLfGTe7YT6FofE6MJmmf0i73OvB0k9EWGulbz3gRnNVtMiyvnY8RaRwwDXlO5873p5wfDFWz+7PDOzdI+lLN7w==", + "requires": { + "cron": "1.7.2", + "uuid": "8.3.2" + } + }, + "@nestjs/schematics": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-7.2.7.tgz", + "integrity": "sha512-71XqMPf7s2P1Q6PVMDLbSLphVWgGDK2CgURVYyreuIWXVSoi9pcPIeO5k0Qb5n5jELlKwdrf66g05U2I6TIxzg==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.1.0", + "@angular-devkit/schematics": "11.1.0", + "fs-extra": "9.1.0", + "pluralize": "8.0.0" + } + }, + "@nestjs/swagger": { + "version": "4.7.13", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-4.7.13.tgz", + "integrity": "sha512-KqFjkx6NOhRVRU4EBu42es/SjopqDwnAfgBCUPer+rKoMUCDMp/hF6dRoH7Z874whPho9pzGcAXBNMxo4/AGdg==", + "requires": { + "@nestjs/mapped-types": "0.3.0", + "lodash": "4.17.20", + "path-to-regexp": "3.2.0" + } + }, + "@nestjs/testing": { + "version": "7.6.12", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-7.6.12.tgz", + "integrity": "sha512-P9iuUo2f3sshbD0Vao+vnIg/tgb7uQ9Y3QSghmIfH1AcjDgGh/V4BdiB0/00F/rcKEPKnHdDBf7pUtzazQawuA==", + "dev": true, + "requires": { + "optional": "0.1.4", + "tslib": "2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==" + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "requires": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@octokit/auth-token": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.2.5.tgz", + "integrity": "sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz", + "integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } + } + }, + "@octokit/graphql": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.0.tgz", + "integrity": "sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ==", + "requires": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-5.1.0.tgz", + "integrity": "sha512-bodZvSYgycbUuuKrC/anCBUExvaSSWzMMFz0xl7pcJujxnmGxvqvcFHktjx1ZOSyeNKLfYF0QCgibaHUGsZTng==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.10.0.tgz", + "integrity": "sha512-71OsKBSMcQEu/6lfVbhv5C5ikU1rn10rKot/WiV7do7fyfElQ2eCUQFogHPbj0ci5lnKAjvahOiMAr6lcvL8Qw==", + "requires": { + "@octokit/types": "^6.10.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz", + "integrity": "sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==" + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.12.0.tgz", + "integrity": "sha512-RgnQ1aoetdOJjZYC37LV5FNlL7GY/v1CdC5dur1Zp/UiADJlbRFbAz/xLx26ovXw67dK7EUtwCghS+6QyiI9RA==", + "requires": { + "@octokit/types": "^6.10.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.14.tgz", + "integrity": "sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.7.1", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } + } + }, + "@octokit/request-error": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz", + "integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.2.0.tgz", + "integrity": "sha512-xsp6bIqL2sb/NmgLXTxw96caegobRw+YHnzdIi70ruquHtPPDW2cBAONhDYMUuAOeXx0JH2auOeplpk4SQJy1w==", + "requires": { + "@octokit/core": "^3.2.3", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "4.12.0" + } + }, + "@octokit/types": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.10.0.tgz", + "integrity": "sha512-aMDo10kglofejJ96edCBIgQLVuzMDyjxmhdgEcoUUD64PlHYSrNsAGqN0wZtoiX4/PCQ3JLA50IpkP1bcKD/cA==", + "requires": { + "@octokit/openapi-types": "^5.1.0" + } + }, + "@otplib/core": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz", + "integrity": "sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==" + }, + "@otplib/plugin-crypto": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz", + "integrity": "sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==", + "requires": { + "@otplib/core": "^12.0.1" + } + }, + "@otplib/plugin-thirty-two": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz", + "integrity": "sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==", + "requires": { + "@otplib/core": "^12.0.1", + "thirty-two": "^1.0.2" + } + }, + "@otplib/preset-default": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/preset-default/-/preset-default-12.0.1.tgz", + "integrity": "sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==", + "requires": { + "@otplib/core": "^12.0.1", + "@otplib/plugin-crypto": "^12.0.1", + "@otplib/plugin-thirty-two": "^12.0.1" + } + }, + "@otplib/preset-v11": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/preset-v11/-/preset-v11-12.0.1.tgz", + "integrity": "sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==", + "requires": { + "@otplib/core": "^12.0.1", + "@otplib/plugin-crypto": "^12.0.1", + "@otplib/plugin-thirty-two": "^12.0.1" + } + }, + "@prisma/cli": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.15.0.tgz", + "integrity": "sha512-sF2mgn5oH5fL9/CKxS0tqojf0rK2BKODlEUqL+2s3YZqvJRSt4iFpiyjgajyd0wyTyv1k9LDHTV0yOD1mXDBsA==", + "dev": true, + "requires": { + "@prisma/engines": "2.15.0-25.e51dc3b5a9ee790a07104bec1c9477d51740fe54" + } + }, + "@prisma/client": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.14.0.tgz", + "integrity": "sha512-0PiiZ2maM9OP/G5mynzftvD4DFrtibeyvZf5U7Be360mtSvgLHQhZ/CHbChb0BVeWeyJuTmAqqni98LcdVgttw==", + "dev": true, + "requires": { + "@prisma/engines-version": "2.14.0-28.5d491261d382a2a5ffdc71de17072b0e409f1cc1" + } + }, + "@prisma/engines": { + "version": "2.15.0-25.e51dc3b5a9ee790a07104bec1c9477d51740fe54", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-2.15.0-25.e51dc3b5a9ee790a07104bec1c9477d51740fe54.tgz", + "integrity": "sha512-AgPxAWtwYhhTNEEsV4lK63HTe9z0GAGL3ofMr2B0TncACmzi9lhdun9TTNie38Oy/3DLfr71TUHKUpV8QjOKUw==", + "dev": true + }, + "@prisma/engines-version": { + "version": "2.14.0-28.5d491261d382a2a5ffdc71de17072b0e409f1cc1", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.14.0-28.5d491261d382a2a5ffdc71de17072b0e409f1cc1.tgz", + "integrity": "sha512-Oz3yNV//9UTJ+ral5tF6Ryb/MLfOE3+2F3/bH4waTbNRDd/zXKoS3jTI3ViU8yRQ1AsOQa9/pV/kXSlxgtAh8Q==", + "dev": true + }, + "@schematics/schematics": { + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@schematics/schematics/-/schematics-0.1102.0.tgz", + "integrity": "sha512-0mN6qGnI31GVNYAKDdZ6ISiJMtN8Z0rekpJ/xNHK/lDNl/QkoJVBHDf68oEcNE8dvWMq86ULpznCdT1IBQ7YFA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "@angular-devkit/schematics": "11.2.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.2.0.tgz", + "integrity": "sha512-sMDacACJbA4pykiqgJf/RdW0damcf4mDqErGgEqs/bGG+SBUb8+wgt4cQnUwwVX5V2nMdvv7f0A84rgR6I3G2w==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "ora": "5.3.0", + "rxjs": "6.6.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "@semantic-release/changelog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-5.0.1.tgz", + "integrity": "sha512-unvqHo5jk4dvAf2nZ3aw4imrlwQ2I50eVVvq9D47Qc3R+keNqepx1vDYwkjF8guFXnOYaYcR28yrZWno1hFbiw==", + "requires": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.4" + } + }, + "@semantic-release/commit-analyzer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-8.0.1.tgz", + "integrity": "sha512-5bJma/oB7B4MtwUkZC2Bf7O1MHfi4gWe4mA+MIQ3lsEV0b422Bvl1z5HRpplDnMLHH3EXMoRdEng6Ds5wUqA3A==", + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.7", + "debug": "^4.0.0", + "import-from": "^3.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@semantic-release/error": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", + "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==" + }, + "@semantic-release/git": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-9.0.0.tgz", + "integrity": "sha512-AZ4Zha5NAPAciIJH3ipzw/WU9qLAn8ENaoVAhD6srRPxTpTzuV3NhNh14rcAo8Paj9dO+5u4rTKcpetOBluYVw==", + "requires": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@semantic-release/github": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-7.2.0.tgz", + "integrity": "sha512-tMRnWiiWb43whRHvbDGXq4DGEbKRi56glDpXDJZit4PIiwDPX7Kx3QzmwRtDOcG+8lcpGjpdPabYZ9NBxoI2mw==", + "requires": { + "@octokit/rest": "^18.0.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "bottleneck": "^2.18.1", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^9.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^2.4.3", + "p-filter": "^2.0.0", + "p-retry": "^4.0.0", + "url-join": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@semantic-release/npm": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.9.tgz", + "integrity": "sha512-VsmmQF3/n7mDbm6AGL0yPD3QNTGsHdinBtkyyerN1eLgvhdGJ/vEeAvmDMARiuf5Ev9cFeCheF0wLyUZNlAkeA==", + "requires": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "execa": "^5.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^5.0.0", + "npm": "^6.14.8", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^4.0.0", + "semver": "^7.1.2", + "tempy": "^1.0.0" + }, + "dependencies": { + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "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==" + }, + "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==" + }, + "normalize-url": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.3.0.tgz", + "integrity": "sha512-9/nOVLYYe/dO/eJeQUNaGUF4m4Z5E7cb9oNTKabH+bNf19mqj60txTcveQxL0GlcWLXCxkOu2/LwL8oW0idIDA==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "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==" + } + } + }, + "@semantic-release/release-notes-generator": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-9.0.1.tgz", + "integrity": "sha512-bOoTiH6SiiR0x2uywSNR7uZcRDl22IpZhj+Q5Bn0v+98MFtOMhCxFhbrKQjhbYoZw7vps1mvMRmFkp/g6R9cvQ==", + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^4.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "debug": "^4.0.0", + "get-stream": "^5.0.0", + "import-from": "^3.0.0", + "into-stream": "^5.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "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==" + } + } + }, + "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==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } + }, + "@sentry/core": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.1.0.tgz", + "integrity": "sha512-57mXkp3NoyxRycXrL+Ec6bYS6UYJZp9tYX0lUp5Ry2M0FxDZ3Q4drkjr8MIQOhBaQXP2ukSX4QTVLGMPm60zMw==", + "requires": { + "@sentry/hub": "6.1.0", + "@sentry/minimal": "6.1.0", + "@sentry/types": "6.1.0", + "@sentry/utils": "6.1.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/hub": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.1.0.tgz", + "integrity": "sha512-JnBSCgNg3VHiMojUl5tCHU8iWPVuE+qqENIzG9A722oJms1kKWBvWl+yQzhWBNdgk5qeAY3F5UzKWJZkbJ6xow==", + "requires": { + "@sentry/types": "6.1.0", + "@sentry/utils": "6.1.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/minimal": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.1.0.tgz", + "integrity": "sha512-g6sfNKenL7wnsr/tibp8nFiMv/XRH0s0Pt4p151npmNI+SmjuUz3GGYEXk8ChCyaKldYKilkNOFdVXJxUf5gZw==", + "requires": { + "@sentry/hub": "6.1.0", + "@sentry/types": "6.1.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/node": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.1.0.tgz", + "integrity": "sha512-yOxYHoPxg8Br19QOsJbonP2uYirv1FFxdNkdeykfO2QBorRUkcirjET5qjRfz73jF1YYtUZBuxwR+f9ZOPqGTg==", + "requires": { + "@sentry/core": "6.1.0", + "@sentry/hub": "6.1.0", + "@sentry/tracing": "6.1.0", + "@sentry/types": "6.1.0", + "@sentry/utils": "6.1.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/tracing": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.1.0.tgz", + "integrity": "sha512-s6a4Ra3hHn4awiNz4fOEK6TCV2w2iLcxdppijcYEB7S/1rJpmqZgHWDicqufbOmVMOLmyKLEQ7w+pZq3TR3WgQ==", + "requires": { + "@sentry/hub": "6.1.0", + "@sentry/minimal": "6.1.0", + "@sentry/types": "6.1.0", + "@sentry/utils": "6.1.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.1.0.tgz", + "integrity": "sha512-kIaN52Fw5K+2mKRaHE2YluJ+F/qMGSUzZXIFDNdC6OUMXQ4TM8gZTrITXs8CLDm7cK8iCqFCtzKOjKK6KyOKAg==" + }, + "@sentry/utils": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.1.0.tgz", + "integrity": "sha512-6JAplzUOS6bEwfX0PDRZBbYRvn9EN22kZfcL0qGHtM9L0QQ5ybjbbVwOpbXgRkiZx++dQbzLFtelxnDhsbFG+Q==", + "requires": { + "@sentry/types": "6.1.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sindresorhus/is": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", + "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==" + }, + "@sindresorhus/slugify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-1.1.0.tgz", + "integrity": "sha512-ujZRbmmizX26yS/HnB3P9QNlNa4+UvHh+rIse3RbOXLp8yl6n1TxB4t7NHggtVgS8QmmOtzXo48kCxZGACpkPw==", + "requires": { + "@sindresorhus/transliterate": "^0.1.1", + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + } + } + }, + "@sindresorhus/transliterate": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-0.1.1.tgz", + "integrity": "sha512-QSdIQ5keUFAZ3KLbfbsntW39ox0Ym8183RqTwBq/ZEFoN3NQAtGV+qWaNdzKpIDHgj9J2CQ2iNDRVU11Zyr7MQ==", + "requires": { + "escape-string-regexp": "^2.0.0", + "lodash.deburr": "^4.1.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@slack/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==", + "requires": { + "@types/node": ">=8.9.0" + } + }, + "@slack/types": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-1.10.0.tgz", + "integrity": "sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg==" + }, + "@slack/web-api": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.0.0.tgz", + "integrity": "sha512-YD1wqWuzrYPf4RQyD7OnYS5lImUmNWn+G5V6Qt0N97fPYxqhT72YJtRdSnsTc3VkH5R5imKOhYxb+wqI9hiHnA==", + "requires": { + "@slack/logger": ">=1.0.0 <3.0.0", + "@slack/types": "^1.7.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=12.0.0", + "axios": "^0.21.1", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + } + } + }, + "@staart/mustache-markdown": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@staart/mustache-markdown/-/mustache-markdown-3.0.3.tgz", + "integrity": "sha512-Mugu0ei0xR7EH0Af8UHkfWa/HQUfZZX5jOAHIIGY442Yh3QyXsChSrdB+wYMyLoipbww9Mdw0GlAZQy7FnSZ1A==", + "requires": { + "@types/marked": "^1.1.0", + "@types/mustache": "^4.0.1", + "marked": "^1.1.1", + "mustache": "^4.0.1" + } + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, + "@types/anymatch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", + "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz", + "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", + "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/bcrypt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-3.0.0.tgz", + "integrity": "sha512-nohgNyv+1ViVcubKBh0+XiNJ3dO8nYu///9aJ4cgSqv70gBL+94SNy/iC2NLzKPT2Zt/QavrOkBVbZRLZmw6NQ==", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/cheerio": { + "version": "0.22.24", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.24.tgz", + "integrity": "sha512-iKXt/cwltGvN06Dd6zwQG1U35edPwId9lmcSeYfcxSNvvNg4vysnFB+iBQNjj06tSVV7MBj0GWMQ7dwb4Z+p8Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, + "@types/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg==", + "dev": true + }, + "@types/dot-object": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/dot-object/-/dot-object-2.1.2.tgz", + "integrity": "sha512-mARrpJofLNe6yhlukeBcznBe8ssZo5ZJ/CJWc3JKmG9L9151s0OHK+mealnkqSgO6cSn1219vND2wgL67Cuqiw==", + "dev": true + }, + "@types/eslint": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", + "integrity": "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", + "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", + "dev": true + }, + "@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz", + "integrity": "sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/fs-extra": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.7.tgz", + "integrity": "sha512-YGq2A6Yc3bldrLUlm17VNWOnUbnEzJ9CMgOeLFtQF3HOCN5lQBO8VyjG00a5acA5NNSM30kHVGp1trZgnVgi1Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" + }, + "@types/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/jsonwebtoken": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", + "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "requires": { + "@types/node": "*" + } + }, + "@types/marked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-1.1.0.tgz", + "integrity": "sha512-j8XXj6/l9kFvCwMyVqozznqpd/nk80krrW+QiIJN60Uu9gX5Pvn4/qPJ2YngQrR3QREPwmrE1f9/EWKVTFzoEw==" + }, + "@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==" + }, + "@types/mustache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.0.1.tgz", + "integrity": "sha512-wH6Tu9mbiOt0n5EvdoWy0VGQaJMHfLIxY/6wS0xLC7CV1taM6gESEzcYy0ZlWvxxiiljYvfDIvz4hHbUUDRlhw==" + }, + "@types/node": { + "version": "14.14.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.32.tgz", + "integrity": "sha512-/Ctrftx/zp4m8JOujM5ZhwzlWLx22nbQJiVqz8/zE15gOeEW+uly3FSX4fGFpcfEvFzXcMCJwq9lGVWgyARXhg==" + }, + "@types/nodemailer": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.0.tgz", + "integrity": "sha512-KY7bFWB0MahRZvVW4CuW83qcCDny59pJJ0MQ5ifvfcjNwPlIT0vW4uARO4u1gtkYnWdhSvURegecY/tzcukJcA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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==" + }, + "@types/nunjucks": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.4.tgz", + "integrity": "sha512-cR65PLlHKW/qxxj840dbNb3ICO+iAVQzaNKJ8TcKOVKFi+QcAkhw9SCY8VFAyU41SmJMs+2nrIN2JGhX+jYb7A==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/passport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz", + "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/passport-local": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.33.tgz", + "integrity": "sha512-+rn6ZIxje0jZ2+DAiWFI8vGG7ZFKB0hXx2cUdMmudSWsigSq6ES7Emso46r4HJk0qCgrZVfI8sJiM7HIYf4SbA==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/passport": "*", + "@types/passport-strategy": "*" + } + }, + "@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "@types/pidusage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/pidusage/-/pidusage-2.0.1.tgz", + "integrity": "sha512-tYYcz/+5v/EGYT83C0pIXrJGOiVBLksQvxgJboG4nGqx/gZTvq0Ro4SkAjECqMk7L4Ww58VWB4j48qeYh4/YJg==", + "dev": true + }, + "@types/prettier": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz", + "integrity": "sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==", + "dev": true + }, + "@types/puppeteer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", + "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/qrcode": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.3.5.tgz", + "integrity": "sha512-92QMnMb9m0ErBU20za5Eqtf4lzUcSkk5w/Cz30q5qod0lWHm2loztmFs2EnCY06yT51GY1+m/oFq2D8qVK2Bjg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "dev": true + }, + "@types/randomcolor": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@types/randomcolor/-/randomcolor-0.5.5.tgz", + "integrity": "sha512-PywdYff3F8lGO3BggkCXaPFH0Ue/2Y7xliihoQNkxCGPJ4w7VTMfgcmSMIE6gOVAEu9Wx42JRSuRREVG3AUrtg==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/request-ip": { + "version": "0.0.35", + "resolved": "https://registry.npmjs.org/@types/request-ip/-/request-ip-0.0.35.tgz", + "integrity": "sha512-FtI7lv1EDaZnWmaCU7ZTwfOpW76EioocaWyiSeWdfW1cDPZYzzij781A5O/UeHQUN9yjtjEcD3StTzZjKG0XEA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/response-time": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.4.tgz", + "integrity": "sha512-+MuxeMIzmXDy/CTw/mPlZO1SWBKbpz9QFo4+KYNXc2iiY0OGH3Ny9DPKT4eSamyDbhWJPfvMMPB4Kh7DapwJhA==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/node": "*" + } + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@types/serve-static": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", + "integrity": "sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/superagent": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz", + "integrity": "sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, + "@types/tapable": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz", + "integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==", + "dev": true + }, + "@types/ua-parser-js": { + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-PsPx0RLbo2Un8+ff2buzYJnZjzwhD3jQHPOG2PtVIeOhkRDddMcKU8vJtHpzzfLB95dkUi0qAkfLg2l2Fd0yrQ==", + "dev": true + }, + "@types/uglify-js": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.12.0.tgz", + "integrity": "sha512-sYAF+CF9XZ5cvEBkI7RtrG9g2GtMBkviTnBxYYyq+8BWvO4QtXfwwR6a2LFwCi4evMKZfpv6U43ViYvv17Wz3Q==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", + "dev": true + }, + "@types/validator": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.0.0.tgz", + "integrity": "sha512-WAy5txG7aFX8Vw3sloEKp5p/t/Xt8jD3GRD9DacnFv6Vo8ubudAsRTXgxpQwU0mpzY/H8U4db3roDuCMjShBmw==" + }, + "@types/webpack": { + "version": "4.41.26", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.26.tgz", + "integrity": "sha512-7ZyTfxjCRwexh+EJFwRUM+CDB2XvgHl4vfuqf1ZKrgGvcS5BrNvPQqJh3tsZ0P6h6Aa1qClVHaJZszLPzpqHeA==", + "dev": true, + "requires": { + "@types/anymatch": "*", + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/webpack-sources": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz", + "integrity": "sha512-LXn/oYIpBeucgP1EIJbKQ2/4ZmpvRl+dlrFdX7+94SKRUV3Evy3FsfMZY318vGhkWUS5MPhtOM3w1/hCOAOXcg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", + "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz", + "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.15.0", + "@typescript-eslint/scope-manager": "4.15.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz", + "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.15.0", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/typescript-estree": "4.15.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.2.tgz", + "integrity": "sha512-SHeF8xbsC6z2FKXsaTb1tBCf0QZsjJ94H6Bo51Y1aVEZ4XAefaw5ZAilMoDPlGghe+qtq7XdTiDlGfVTOmvA+Q==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.15.2", + "@typescript-eslint/types": "4.15.2", + "@typescript-eslint/typescript-estree": "4.15.2", + "debug": "^4.1.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.2.tgz", + "integrity": "sha512-Zm0tf/MSKuX6aeJmuXexgdVyxT9/oJJhaCkijv0DvJVT3ui4zY6XYd6iwIo/8GEZGy43cd7w1rFMiCLHbRzAPQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.2", + "@typescript-eslint/visitor-keys": "4.15.2" + } + }, + "@typescript-eslint/types": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.2.tgz", + "integrity": "sha512-r7lW7HFkAarfUylJ2tKndyO9njwSyoy6cpfDKWPX6/ctZA+QyaYscAHXVAfJqtnY6aaTwDYrOhp+ginlbc7HfQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.2.tgz", + "integrity": "sha512-cGR8C2g5SPtHTQvAymEODeqx90pJHadWsgTtx6GbnTWKqsg7yp6Eaya9nFzUd4KrKhxdYTTFBiYeTPQaz/l8bw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.2", + "@typescript-eslint/visitor-keys": "4.15.2", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.2.tgz", + "integrity": "sha512-TME1VgSb7wTwgENN5KVj4Nqg25hP8DisXxNBojM4Nn31rYaNDIocNm5cmjOFfh42n7NVERxWrDFoETO/76ePyg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.2", + "eslint-visitor-keys": "^2.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", + "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0" + } + }, + "@typescript-eslint/types": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", + "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz", + "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", + "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.1.tgz", + "integrity": "sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/wast-parser": "1.9.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz", + "integrity": "sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz", + "integrity": "sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz", + "integrity": "sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz", + "integrity": "sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.1" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz", + "integrity": "sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz", + "integrity": "sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz", + "integrity": "sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz", + "integrity": "sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz", + "integrity": "sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.1.tgz", + "integrity": "sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.1.tgz", + "integrity": "sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz", + "integrity": "sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/helper-wasm-section": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1", + "@webassemblyjs/wasm-opt": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1", + "@webassemblyjs/wast-printer": "1.9.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz", + "integrity": "sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/ieee754": "1.9.1", + "@webassemblyjs/leb128": "1.9.1", + "@webassemblyjs/utf8": "1.9.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz", + "integrity": "sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz", + "integrity": "sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-api-error": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/ieee754": "1.9.1", + "@webassemblyjs/leb128": "1.9.1", + "@webassemblyjs/utf8": "1.9.1" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz", + "integrity": "sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/floating-point-hex-parser": "1.9.1", + "@webassemblyjs/helper-api-error": "1.9.1", + "@webassemblyjs/helper-code-frame": "1.9.1", + "@webassemblyjs/helper-fsm": "1.9.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz", + "integrity": "sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/wast-parser": "1.9.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", + "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "agentkeepalive": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.3.tgz", + "integrity": "sha512-wn8fw19xKZwdGPO47jivonaHRTd+nGOMP1z11sgGeQzDy2xd5FG0R67dIMcKHDE2cJ5y+YXV30XVGUBPRSY7Hg==", + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=" + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "requires": { + "array-filter": "^1.0.0" + } + }, + "aws-elasticsearch-connector": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/aws-elasticsearch-connector/-/aws-elasticsearch-connector-9.0.3.tgz", + "integrity": "sha512-jnzTHp3cIe1VxnQAO0t6sUIZ19WFe+LtXnX0FnHeoJUvOnrKSI5ooGGlZPCud2jmqVPK4L4c0w8XtiBjHyTk6g==", + "requires": { + "aws4": "^1.11.0" + } + }, + "aws-sdk": { + "version": "2.850.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.850.0.tgz", + "integrity": "sha512-RqPeSKe1JlhTUL9+xUsp771ZtMY7JICoQEnFJuJ+JVqGyILhg95L4t8S5KnznUfWYc0pcpTiHKLmPteHyHS3pw==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz", + "integrity": "sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg==", + "requires": { + "node-addon-api": "^3.0.0", + "node-pre-gyp": "0.15.0" + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "before-after-hook": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.1.tgz", + "integrity": "sha512-5ekuQOvO04MDj7kYZJaMab2S8SPjGJbotVNyv7QYFCOAwrGZs/YnoDNlh1U+m5hl7H2D/+n0taaAV/tfyd3KMA==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.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==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "dependencies": { + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==" + } + } + }, + "caniuse-lite": { + "version": "1.0.30001191", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001191.tgz", + "integrity": "sha512-xJJqzyd+7GCJXkcoBiQ1GuxEiOBCLQ0aVW9HMekifZsAVGdj5eJ4mFB9fEhSHipq9IOk/QXFJUiIr9lZT+EsGw==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cheerio": { + "version": "1.0.0-rc.5", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz", + "integrity": "sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==", + "requires": { + "cheerio-select-tmp": "^0.1.0", + "dom-serializer": "~1.2.0", + "domhandler": "^4.0.0", + "entities": "~2.1.0", + "htmlparser2": "^6.0.0", + "parse5": "^6.0.0", + "parse5-htmlparser2-tree-adapter": "^6.0.0" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + } + } + }, + "cheerio-select-tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select-tmp/-/cheerio-select-tmp-0.1.0.tgz", + "integrity": "sha512-kx/pq9hxLo6FhjiYqUheSOV0Eb729ZwkXXPrPTeK6kl/VMgaUlsYoAOv3nOJZcHk++V9pI17YNNngtbLVPTB9A==", + "requires": { + "css-select": "^3.1.2", + "css-what": "^4.0.0", + "domelementtype": "^2.1.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.4" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "circularbuffer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/circularbuffer/-/circularbuffer-0.1.1.tgz", + "integrity": "sha512-thWEWBSrqL90EA/AbOcpT3HuT1RO5wb7P+/YIe1ZMlPDd0139O/zxGy38iSWTm7W2mphPPtU/2SJyvEF60Jjig==" + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "class-transformer": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.3.2.tgz", + "integrity": "sha512-9QY6QXBH/+Gt1C3HBmJCrgY6+EFpIa6aLjfDnlXFx0zQl/HjrCE7qoaI0srNrxpMIfsobCpgUdDG5JYtJOpVsw==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "class-validator": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.12.2.tgz", + "integrity": "sha512-TDzPzp8BmpsbPhQpccB3jMUE/3pK0TyqamrK0kcx+ZeFytMA+O6q87JZZGObHHnoo9GM8vl/JppIyKWeEA/EVw==", + "requires": { + "@types/validator": "13.0.0", + "google-libphonenumber": "^3.2.8", + "tslib": ">=1.9.0", + "validator": "13.0.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz", + "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==", + "dev": true + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "requires": { + "colors": "1.0.3" + }, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + } + } + }, + "cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "cloudinary": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-1.24.0.tgz", + "integrity": "sha512-bILjdVB/FCv5Zyypuhp5IdKeoDDMbA/8ybq8kILV9At5l2AV3ZwdjvYSNF04hTg9+Zan1d5id6LmV5liGsx1bw==", + "requires": { + "cloudinary-core": "^2.10.2", + "core-js": "3.6.5", + "lodash": "^4.17.11", + "q": "^1.5.1" + }, + "dependencies": { + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + } + } + }, + "cloudinary-core": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/cloudinary-core/-/cloudinary-core-2.11.3.tgz", + "integrity": "sha512-ZRnpjSgvx+LbSf+aEz5NKzxDB4Z0436aY/0BSDa90kAHiwAyd84VyEi95I74SE80e15Ri9t5S2xtksTXpzk9Xw==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "optional": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "conventional-changelog-angular": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "dependencies": { + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + } + } + }, + "conventional-changelog-writer": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz", + "integrity": "sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A==", + "requires": { + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz", + "integrity": "sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ==", + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^2.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cron": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/cron/-/cron-1.7.2.tgz", + "integrity": "sha512-+SaJ2OfeRvfQqwXQ2kgr0Y5pzBR/lijf5OpnnaruwWnmI799JfWr2jN2ItOV9s3A/+TFOt6mxvKzQq5F0Jp6VQ==", + "requires": { + "moment-timezone": "^0.5.x" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.3.1.tgz", + "integrity": "sha512-5j88ECEn6h17UePrLi6pn1JcLtAiANa3KExyr9y9Z5vo2mv56Gh3I4Aja/B9P9uyMwyxNHAHWv+nE72f30T5Dg==", + "requires": { + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } + }, + "css-select": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", + "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^4.0.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.3", + "nth-check": "^2.0.0" + } + }, + "css-what": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", + "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==" + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" + }, + "dayjs": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz", + "integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + } + } + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "deep-equal": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", + "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "requires": { + "call-bind": "^1.0.0", + "es-get-iterator": "^1.1.1", + "get-intrinsic": "^1.0.1", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.1.1", + "isarray": "^2.0.5", + "object-is": "^1.1.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "devtools-protocol": { + "version": "0.0.818844", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz", + "integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==" + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "dijkstrajs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz", + "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs=" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "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==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "requires": { + "domelementtype": "^2.1.0" + } + }, + "domutils": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" + } + }, + "dot-object": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-2.1.4.tgz", + "integrity": "sha512-7FXnyyCLFawNYJ+NhkqyP9Wd2yzuo+7n9pGiYpkmXCTYa8Ci2U0eUNDVg5OuO5Pm6aFXI2SWN8/N/w7SJWu1WA==", + "requires": { + "commander": "^4.0.0", + "glob": "^7.1.5" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.3.672", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.672.tgz", + "integrity": "sha512-gFQe7HBb0lbOMqK2GAS5/1F+B0IMdYiAgB9OT/w1F4M7lgJK2aNOMNOM622aEax+nS1cTMytkiT0uMOkbtFmHw==", + "dev": true + }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "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==" + }, + "env-ci": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.0.2.tgz", + "integrity": "sha512-Xc41mKvjouTXD3Oy9AqySz1IeyvJvHZ20Twf5ZLYbNpPPIuCnL/qHCmNlD01LoNy0JTunw9HPYVptD19Ac7Mbw==", + "requires": { + "execa": "^4.0.0", + "java-properties": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "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-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" + } + }, + "es-get-iterator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", + "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.1", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.20.0.tgz", + "integrity": "sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.3.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz", + "integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==", + "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", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + } + } + }, + "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", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fastq": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", + "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "requires": { + "semver-regex": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "follow-redirects": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", + "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.1.0.tgz", + "integrity": "sha512-xLNufWQ1dfQUdZe48TGQlER/0OkcMnUB6lfbN9Tt13wsYyo+2DwcCbnOaPBo1PoFow/WL8pJPktGIdbJaHxAnw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formdata-node": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-2.4.0.tgz", + "integrity": "sha512-eikLJEYMMtrc8/tRjUOhwJYoxQVb4R3X+Qlj1gv7tDgihpL8HMnsLLhUyjV8Y2iUm03RmpGmn/o4LyzGX396Uw==", + "requires": { + "mime-types": "2.1.27" + } + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs-monkey": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", + "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "geolite2-redist": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/geolite2-redist/-/geolite2-redist-1.0.7.tgz", + "integrity": "sha512-NrsPDYUU7OVTtZzj5McnBI7b2n8teS+zgX3IXSd3qxF9M2OaDA4SeF1loWzDyzkQzPFr8JHEYYIBTD0sQGQ7ug==", + "requires": { + "rimraf": "^3.0.2", + "tar": "^6.0.2" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "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.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "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", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-log-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", + "integrity": "sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=", + "requires": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=", + "requires": { + "through2": "~2.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "git-up": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", + "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^5.0.0" + } + }, + "git-url-parse": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.0.tgz", + "integrity": "sha512-KlIa5jvMYLjXMQXkqpFzobsyD/V2K5DRHl5OAf+6oDFPlPLxrGDVQlIdI63c4/Kt6kai4kALENSALlzTGST3GQ==", + "requires": { + "git-up": "^4.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "google-libphonenumber": { + "version": "3.2.14", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.14.tgz", + "integrity": "sha512-4r7mQRbk7EUYV1gyfP1SInYuQsjuDtRXCGLSotxeYDJaj/aF1xFO5PV/GSQeIxXWhIw050DujROICvWpZ1XYRw==" + }, + "got": { + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.1.tgz", + "integrity": "sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "helmet": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.4.1.tgz", + "integrity": "sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw==" + }, + "hibp": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/hibp/-/hibp-10.0.1.tgz", + "integrity": "sha512-ol2/tyOf/8fr9i+8BR8uLXCIgaXUN0EpTeY3uBRyN/6jrAvpa91P8pvsiJ8XxNXDEscjNQTY5clxlcroLxHjhA==", + "requires": { + "isomorphic-unfetch": "^3.1.0", + "jssha": "^3.2.0" + } + }, + "hook-std": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", + "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==" + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, + "hpagent": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-0.1.1.tgz", + "integrity": "sha512-IxJWQiY0vmEjetHdoE9HZjD4Cx+mYTr25tR7JCxXaiI3QxW0YqYyM11KyZbHufoa/piWhMb2+D3FGpMgmA2cFQ==" + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "htmlparser2": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz", + "integrity": "sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.4.4", + "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", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "into-stream": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", + "integrity": "sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==", + "requires": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + } + }, + "ip-anonymize": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ip-anonymize/-/ip-anonymize-0.1.0.tgz", + "integrity": "sha512-cZJu+N5JKKFGMK0eEQWNaQMn2EhCysciVM6eotCJwfqotj16BTfVchKsJCH6mQAT9N0GC7oWRcsZ6Lb8dDiwTA==" + }, + "ip-range-check": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ip-range-check/-/ip-range-check-0.2.0.tgz", + "integrity": "sha512-oaM3l/3gHbLlt/tCWLvt0mj1qUaI+STuRFnUvARGCujK9vvU61+2JsDpmkMzR4VsJhuFXWWgeKKVnwwoFfzCqw==", + "requires": { + "ipaddr.js": "^1.0.1" + } + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-bigint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" + }, + "is-ssh": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.2.tgz", + "integrity": "sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ==", + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "requires": { + "text-extensions": "^1.0.0" + } + }, + "is-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", + "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "has-symbols": "^1.0.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==", + "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" + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==" + }, + "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-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "is_js": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/is_js/-/is_js-0.9.0.tgz", + "integrity": "sha1-CrlFQFArp6+iTIVqqYVWFmnpxS0=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "requires": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "requires": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + } + }, + "issue-regex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/issue-regex/-/issue-regex-3.1.0.tgz", + "integrity": "sha512-0RHjbtw9QXeSYnIEY5Yrp2QZrdtz21xBDV9C/GIlY2POmgoS6a7qjkYS5siRKXScnuAj5/SPv1C3YForNCHTJA==" + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" + }, + "java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==" + }, + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "requires": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "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" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jssha": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", + "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==" + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "dependencies": { + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.deburr": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", + "integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=" + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.has": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", + "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=" + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, + "macos-release": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", + "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==", + "dev": true + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "marked": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.2.tgz", + "integrity": "sha512-5jjKHVl/FPo0Z6ocP3zYhKiJLzkwJAw4CZoLjv57FkvbUuwOX4LIBBGGcXjAY6ATcd1q9B8UTj5T9Umauj0QYQ==" + }, + "marked-terminal": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.1.0.tgz", + "integrity": "sha512-5KllfAOW02WS6hLRQ7cNvGOxvKW1BKuXELH4EtbWfyWgxQhROoMxEvuQ/3fTgkNjledR0J48F4HbapvYp1zWkQ==", + "requires": { + "ansi-escapes": "^4.3.1", + "cardinal": "^2.1.1", + "chalk": "^4.0.0", + "cli-table": "^0.3.1", + "node-emoji": "^1.10.0", + "supports-hyperlinks": "^2.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "maxmind": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.1.tgz", + "integrity": "sha512-0CxAgwWIwQy4zF1/qCMOeUPleMTYgfnsuIsZ4Otzx6hzON4PCqivPiH6Kz7iWrC++KOGCbSB3nxkJMvDEdWt6g==", + "requires": { + "mmdb-lib": "1.2.0", + "tiny-lru": "7.0.6" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.0.0.tgz", + "integrity": "sha512-qrcJOe6uD+EW8Wrci1Vdiua/15Xw3n/QnaNXE7varnB6InxSk7nu3/i5jfy3S6kWxr8WYJ6R1o0afMUtvorTsA==", + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" + } + } + }, + "memfs": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", + "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", + "dev": true, + "requires": { + "fs-monkey": "1.0.1" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "meow": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "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": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "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==" + } + } + }, + "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==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==" + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "mmdb-lib": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-1.2.0.tgz", + "integrity": "sha512-3XYebkStxqCgWJjsmT9FCaE19Yi4Tvs2SBPKhUks3rJJh52oF1AKAd9kei+LTutud3a6RCZ0o2Um96Fn7o3zVA==" + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==" + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.32", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", + "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", + "requires": { + "moment": ">= 2.9.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, + "mustache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.1.tgz", + "integrity": "sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA==" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "needle": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", + "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-addon-api": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.2.tgz", + "integrity": "sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg==" + }, + "node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node-pre-gyp": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", + "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.3", + "needle": "^2.5.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "node-releases": { + "version": "1.1.70", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", + "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", + "dev": true + }, + "nodemailer": { + "version": "6.4.18", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.18.tgz", + "integrity": "sha512-ht9cXxQ+lTC+t00vkSIpKHIyM4aXIsQ1tcbQCn5IOnxYHi81W2XOaU66EQBFFpbtzLEBTC94gmkbD4mGZQzVpA==" + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-email": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/normalize-email/-/normalize-email-1.1.1.tgz", + "integrity": "sha512-d5gghFO3BceKeZ+csXTsw2/qnuqZa5rHabXTtLg6XYEqlm25G7g0TNrPlB72+6qXFitSTHf+lHcJhm6bQurC+Q==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, + "npm": { + "version": "6.14.9", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.9.tgz", + "integrity": "sha512-yHi1+i9LyAZF1gAmgyYtVk+HdABlLy94PMIDoK1TRKWvmFQAt5z3bodqVwKvzY0s6dLqQPVsRLiwhJfNtiHeCg==", + "requires": { + "JSONStream": "^1.3.5", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "^2.0.0", + "archy": "~1.0.0", + "bin-links": "^1.1.8", + "bluebird": "^3.5.5", + "byte-size": "^5.0.1", + "cacache": "^12.0.3", + "call-limit": "^1.1.1", + "chownr": "^1.1.4", + "ci-info": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.1", + "cmd-shim": "^3.0.3", + "columnify": "~1.5.4", + "config-chain": "^1.1.12", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.3.1", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.8.8", + "iferr": "^1.0.2", + "imurmurhash": "*", + "infer-owner": "^1.0.4", + "inflight": "~1.0.6", + "inherits": "^2.0.4", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^3.0.0", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^4.0.8", + "libnpm": "^3.0.1", + "libnpmaccess": "^3.0.2", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "libnpx": "^10.2.4", + "lock-verify": "^2.1.0", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^5.1.1", + "meant": "^1.0.2", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.5", + "move-concurrently": "^1.0.1", + "node-gyp": "^5.1.0", + "nopt": "^4.0.3", + "normalize-package-data": "^2.5.0", + "npm-audit-report": "^1.3.3", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "^3.0.2", + "npm-lifecycle": "^3.1.5", + "npm-package-arg": "^6.1.1", + "npm-packlist": "^1.4.8", + "npm-pick-manifest": "^3.0.2", + "npm-profile": "^4.0.4", + "npm-registry-fetch": "^4.0.7", + "npm-user-validate": "^1.0.1", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "^1.5.1", + "osenv": "^0.1.5", + "pacote": "^9.5.12", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.8.2", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "^1.0.5", + "read-installed": "~4.0.3", + "read-package-json": "^2.1.1", + "read-package-tree": "^5.3.1", + "readable-stream": "^3.6.0", + "readdir-scoped-modules": "^1.1.0", + "request": "^2.88.0", + "retry": "^0.12.0", + "rimraf": "^2.7.1", + "safe-buffer": "^5.1.2", + "semver": "^5.7.1", + "sha": "^3.0.0", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.1", + "stringify-package": "^1.0.1", + "tar": "^4.4.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "^1.1.1", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.3", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.7.0", + "write-file-atomic": "^2.4.3" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.5", + "bundled": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.3.0", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "bundled": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true + }, + "aws4": { + "version": "1.8.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.8", + "bundled": true, + "requires": { + "bluebird": "^3.5.3", + "cmd-shim": "^3.0.0", + "gentle-fs": "^2.3.0", + "graceful-fs": "^4.1.15", + "npm-normalize-package-bin": "^1.0.0", + "write-file-atomic": "^2.3.0" + } + }, + "bluebird": { + "version": "3.5.5", + "bundled": true + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "5.0.1", + "bundled": true + }, + "cacache": { + "version": "12.0.3", + "bundled": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.1", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true + }, + "ci-info": { + "version": "2.0.0", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.10", + "bundled": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.1", + "bundled": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "bundled": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "3.0.3", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.3.3", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "bundled": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.5", + "bundled": true, + "requires": { + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "bundled": true + } + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "bundled": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.1", + "bundled": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "emoji-regex": { + "version": "7.0.3", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.0", + "bundled": true + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "bundled": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "bundled": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } + } + }, + "extend": { + "version": "3.0.2", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.5.1", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.3.2", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "requires": { + "minipass": "^2.6.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "5.0.0", + "bundled": true + }, + "gentle-fs": { + "version": "2.3.1", + "bundled": true, + "requires": { + "aproba": "^1.1.2", + "chownr": "^1.1.2", + "cmd-shim": "^3.0.3", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "infer-owner": "^1.0.4", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "bundled": true + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "bundled": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + }, + "har-validator": { + "version": "5.1.5", + "bundled": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "bundled": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true + } + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-symbols": { + "version": "1.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "2.8.8", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "bundled": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-callable": { + "version": "1.1.4", + "bundled": true + }, + "is-ci": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ci-info": "^1.5.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "bundled": true + } + } + }, + "is-cidr": { + "version": "3.0.0", + "bundled": true, + "requires": { + "cidr-regex": "^2.0.10" + } + }, + "is-date-object": { + "version": "1.0.1", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-regex": { + "version": "1.0.4", + "bundled": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "is-symbol": { + "version": "1.0.2", + "bundled": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "libcipm": { + "version": "4.0.8", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "ini": "^1.3.5", + "lock-verify": "^2.1.0", + "mkdirp": "^0.5.1", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^9.1.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpm": { + "version": "3.0.1", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.3", + "find-npm-prefix": "^1.0.2", + "libnpmaccess": "^3.0.2", + "libnpmconfig": "^1.2.1", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmpublish": "^1.1.2", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "npm-profile": "^4.0.2", + "npm-registry-fetch": "^4.0.0", + "npmlog": "^4.1.2", + "pacote": "^9.5.3", + "read-package-json": "^2.0.13", + "stringify-package": "^1.0.0" + } + }, + "libnpmaccess": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + } + } + }, + "libnpmhook": { + "version": "5.0.3", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmorg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmpublish": { + "version": "1.1.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + } + }, + "libnpmsearch": { + "version": "2.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmteam": { + "version": "1.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpx": { + "version": "10.2.4", + "bundled": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^14.2.3" + } + }, + "lock-verify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "^6.1.0", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true + }, + "lru-cache": { + "version": "5.1.1", + "bundled": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "version": "5.0.2", + "bundled": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "meant": { + "version": "1.0.2", + "bundled": true + }, + "mime-db": { + "version": "1.35.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.19", + "bundled": true, + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "requires": { + "minipass": "^2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "bundled": true, + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } + } + }, + "ms": { + "version": "2.1.1", + "bundled": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "5.1.0", + "bundled": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.1.2", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "semver": "^5.7.1", + "tar": "^4.4.12", + "which": "^1.3.1" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "bundled": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "npm-audit-report": { + "version": "1.3.3", + "bundled": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.2", + "bundled": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "3.1.5", + "bundled": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.15", + "node-gyp": "^5.0.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.1" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true + }, + "npm-package-arg": { + "version": "6.1.1", + "bundled": true, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "4.0.4", + "bundled": true, + "requires": { + "aproba": "^1.1.2 || 2", + "figgy-pudding": "^3.4.1", + "npm-registry-fetch": "^4.0.0" + } + }, + "npm-registry-fetch": { + "version": "4.0.7", + "bundled": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "bundled": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "object-keys": { + "version": "1.0.12", + "bundled": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "bundled": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.1", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "9.5.12", + "bundled": true, + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-normalize-package-bin": "^1.0.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^3.0.0", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "path-parse": { + "version": "1.0.6", + "bundled": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true + }, + "pify": { + "version": "3.0.0", + "bundled": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.1", + "bundled": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "psl": { + "version": "1.1.29", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.5.2", + "bundled": true + }, + "query-string": { + "version": "6.8.2", + "bundled": true, + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.5", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "version": "2.1.1", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.3.1", + "bundled": true, + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "bundled": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.88.0", + "bundled": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "2.0.0", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "^1.1.1" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.7.1", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "3.0.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true + }, + "socks": { + "version": "2.3.3", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "bundled": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "bundled": true + }, + "split-on-first": { + "version": "1.1.0", + "bundled": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "tough-cookie": { + "version": "2.4.3", + "bundled": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "uri-js": { + "version": "4.4.0", + "bundled": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "bundled": true + } + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "util-promisify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.3", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.1", + "bundled": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "bundled": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.4.3", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true + }, + "yargs": { + "version": "14.2.3", + "bundled": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "15.0.1", + "bundled": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "bundled": true + } + } + } + } + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "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==", + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", + "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nunjucks": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + } + } + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-hash": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", + "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==" + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + }, + "object-is": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", + "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^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==", + "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", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optional": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz", + "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.0.tgz", + "integrity": "sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg==", + "dev": true, + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "otplib": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz", + "integrity": "sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==", + "requires": { + "@otplib/core": "^12.0.1", + "@otplib/preset-default": "^12.0.1", + "@otplib/preset-v11": "^12.0.1" + } + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-each-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==" + }, + "p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "requires": { + "p-map": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + } + }, + "p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==" + }, + "p-retry": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.4.0.tgz", + "integrity": "sha512-gVB/tBsG+3AHI1SyDHRrX6n9ZL0Bcbifps9W9/Bgu3Oyu4/OrAh8SvDzDsvpP0oxfCt3oWNT+0fQ9LyUGwBTLg==", + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "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==", + "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" + } + }, + "parse-path": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.2.tgz", + "integrity": "sha512-HSqVz6iuXSiL8C1ku5Gl1Z5cwDd9Wo0q8CoffdAghP6bz8pJa1tcMC+m4N+z6VAS8QdksnIGq1TB6EgR4vPR6w==", + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0" + } + }, + "parse-url": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.2.tgz", + "integrity": "sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA==", + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^3.3.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + }, + "dependencies": { + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" + } + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "passport": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", + "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "requires": { + "safe-buffer": "^5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" + }, + "pop-iterate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pop-iterate/-/pop-iterate-1.0.1.tgz", + "integrity": "sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "puppeteer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz", + "integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==", + "requires": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.818844", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^4.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "dependencies": { + "agent-base": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "https-proxy-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", + "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "requires": { + "agent-base": "5", + "debug": "4" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "q": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/q/-/q-2.0.3.tgz", + "integrity": "sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=", + "requires": { + "asap": "^2.0.0", + "pop-iterate": "^1.0.1", + "weak-map": "^1.0.5" + } + }, + "qrcode": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", + "requires": { + "buffer": "^5.4.3", + "buffer-alloc": "^1.2.0", + "buffer-from": "^1.1.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^13.2.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "buffer": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.1.tgz", + "integrity": "sha512-2z15UUHpS9/3tk9mY/q+Rl3rydOi7yMp5XWNQnRvoz+mJwiv8brqYwp9a+nOCtma6dwuEIxljD8W3ysVBZ05Vg==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "query-string": { + "version": "6.13.8", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.8.tgz", + "integrity": "sha512-jxJzQI2edQPE/NPUOusNjO/ZOGqr1o2OBa/3M00fU76FsLXDVbJDv/p7ng5OdQyorKrkRz1oqfwmbe5MAMePQg==", + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomcolor": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/randomcolor/-/randomcolor-0.6.2.tgz", + "integrity": "sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "rate-limiter-flexible": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.2.1.tgz", + "integrity": "sha512-rxCP6kDDdn0cZmVqVlF06yLU+mG3TuwaHV/fUIw3OQyYhza7pzVBtdMhUmfXbBzMS+O464XP+x33pfTDGRGYVA==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + } + } + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "dependencies": { + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", + "requires": { + "esprima": "~4.0.0" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "requires": { + "rc": "^1.2.8" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-ip": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-2.1.3.tgz", + "integrity": "sha512-J3qdE/IhVM3BXkwMIVO4yFrvhJlU3H7JH16+6yHucadT4fePnR8dyh+vEs6FIx0S2x5TCt2ptiPfHcn0sqhbYQ==", + "requires": { + "is_js": "^0.9.0" + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==" + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "response-time": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", + "integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=", + "requires": { + "depd": "~1.1.0", + "on-headers": "~1.0.1" + } + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "retry-axios": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-2.4.0.tgz", + "integrity": "sha512-rK7UBYgbrNoVothbSmM0tEm9DIiXapmVUrnUYn+d9AuQvF0AY5RkJU2FQvlufe9hlFwrCdDhrJTwiyRtR7wUaA==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "rootpath": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/rootpath/-/rootpath-0.1.2.tgz", + "integrity": "sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=" + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==" + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "scmp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", + "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" + }, + "secure-json-parse": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.3.0.tgz", + "integrity": "sha512-kEyTf2cpnuqp7Aiem+yz3QWgm58pYbLlYg4TnVWChZkUBQTcolYZIYRQXmXvEtGJGJ532LREyc8d7pbu9utu7A==" + }, + "semantic-release": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-17.3.0.tgz", + "integrity": "sha512-enhDayMZRP4nWcWAMBFHHB7THRaIcRdUAZv3lxd65pXs2ttzay7IeCvRRrGayRWExtnY0ulwRz5Ycp88Dv/UeQ==", + "requires": { + "@semantic-release/commit-analyzer": "^8.0.0", + "@semantic-release/error": "^2.2.0", + "@semantic-release/github": "^7.0.0", + "@semantic-release/npm": "^7.0.0", + "@semantic-release/release-notes-generator": "^9.0.0", + "aggregate-error": "^3.0.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.0.0", + "env-ci": "^5.0.0", + "execa": "^4.0.0", + "figures": "^3.0.0", + "find-versions": "^3.0.0", + "get-stream": "^5.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^2.0.0", + "hosted-git-info": "^3.0.0", + "lodash": "^4.17.15", + "marked": "^1.0.0", + "marked-terminal": "^4.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "p-reduce": "^2.0.0", + "read-pkg-up": "^7.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^3.1.1", + "signale": "^1.2.1", + "yargs": "^15.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "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==" + } + } + }, + "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==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } + }, + "semantic-release-gitmoji": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/semantic-release-gitmoji/-/semantic-release-gitmoji-1.3.4.tgz", + "integrity": "sha512-uuuEmlhCwnSm3KMhu/bO2TMza/Fh8tZ8XVarHBVOVReSoGlJZKXn/TI1BSCgs2iPaxEJM3yJQ/zVCJISwfPtrQ==", + "requires": { + "dateformat": "^3.0.3", + "debug": "^4.1.1", + "emoji-regex": "^9.0.0", + "git-url-parse": "^11.1.2", + "handlebars": "^4.7.6", + "issue-regex": "^3.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.mergewith": "^4.6.2", + "lodash.uniq": "^4.5.0", + "node-emoji": "^1.10.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "emoji-regex": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", + "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "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==", + "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==" + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "dependencies": { + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + } + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "requires": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "dependencies": { + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + } + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk=" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "requires": { + "through2": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", + "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + }, + "string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.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==", + "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.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.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==", + "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_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "stripe": { + "version": "8.137.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-8.137.0.tgz", + "integrity": "sha512-UlxXjff6O+0hGY7DRZZnepZegXfi8KoYKuW4fgMlNIiyvKR/G8EjL13uaqFI31vmVm0WxNvjrBiHd9DFX9rLgA==", + "requires": { + "@types/node": ">=8.1.0", + "qs": "^6.6.0" + } + }, + "superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "mime": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", + "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "supertest": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.3.tgz", + "integrity": "sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ==", + "dev": true, + "requires": { + "methods": "^1.1.2", + "superagent": "^6.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "swagger-ui-dist": { + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.38.0.tgz", + "integrity": "sha512-sselV8VY6f1BBauY9Sdmwz0jVaWTnGuHQWei7BaTpiUrLcoEUdmmK5bKefLXiwq+dx//es2S8mOvUS+tcXDsKg==" + }, + "swagger-ui-express": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz", + "integrity": "sha512-Xs2BGGudvDBtL7RXcYtNvHsFtP1DBFPMJFRxHe5ez/VG/rzVOEjazJOOSc/kSCyxreCTKfJrII6MJlL9a6t8vw==", + "requires": { + "swagger-ui-dist": "^3.18.1" + } + }, + "symbol-observable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-3.0.0.tgz", + "integrity": "sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q==", + "dev": true + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "table": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "dev": true, + "requires": { + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ajv": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", + "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + }, + "tempy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.0.tgz", + "integrity": "sha512-eLXG5B1G0mRPHmgH2WydPl5v4jH35qEn3y/rA/aahKhIa91Pn119SsU7n7v/433gtT9ONzC8ISvNHIh2JSTm0w==", + "requires": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.0.tgz", + "integrity": "sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==", + "dev": true, + "requires": { + "jest-worker": "^26.6.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.5.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thirty-two": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", + "integrity": "sha1-TKL//AKlEpDSdEueP1V2k8prYno=" + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "tiny-lru": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", + "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==" + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=" + }, + "ts-jest": { + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.0.tgz", + "integrity": "sha512-Ya4IQgvIFNa2Mgq52KaO8yBw2W8tWp61Ecl66VjF0f5JaV8u50nGoptHVILOPGoI7SDnShmEqnYQEmyHdQ+56g==", + "dev": true, + "requires": { + "@types/jest": "26.x", + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "dependencies": { + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, + "ts-loader": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.16.tgz", + "integrity": "sha512-Cr9ywsgg1n8cjGjIogHLPlqe3WJUHzuJaqwNo5I596KpIqekKzxvSENbrXeOypHcXSPPsr8hV6mglngyXvcKrg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tsconfig-paths-webpack-plugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.3.0.tgz", + "integrity": "sha512-MpQeZpwPY4gYASCUjY4yt2Zj8yv86O8f++3Ai4o0yI0fUC6G1syvnL9VuY71PBgimRYDQU47f12BEmJq9wRaSw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "tsconfig-paths": "^3.4.0" + } + }, + "tslib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", + "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "twilio": { + "version": "3.56.0", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-3.56.0.tgz", + "integrity": "sha512-SXnYInB5P00UP2umFkOow0e++2UoWpabPkcltCzsKdsZQLI+AfLd0xyVP3dF8Z7An8i78GvnFTC/bSYXhrddzA==", + "requires": { + "axios": "^0.21.1", + "dayjs": "^1.8.29", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.19", + "q": "2.0.x", + "qs": "^6.9.4", + "rootpath": "^0.1.2", + "scmp": "^2.1.0", + "url-parse": "^1.4.7", + "xmlbuilder": "^13.0.2" + }, + "dependencies": { + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", + "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.24", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz", + "integrity": "sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==" + }, + "uglify-js": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.3.tgz", + "integrity": "sha512-wDRziHG94mNj2n3R864CvYw/+pc9y/RNImiTyrrf8BzgWn75JgFSwYvXrtZQMnMnOp/4UTrf3iCSQxSStPiByA==", + "optional": true + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, + "unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + }, + "dependencies": { + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + } + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" + }, + "url-parse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", + "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "v8-to-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz", + "integrity": "sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validator": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz", + "integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", + "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "weak-map": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", + "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "webpack": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.0.tgz", + "integrity": "sha512-ubWv7iP54RqAC/VjixgpnLLogCFbAfSOREcSWnnOlZEU8GICC5eKmJSu6YEnph2N2amKqY9rvxSwgyHxVqpaRw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.45", + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-module-context": "1.9.1", + "@webassemblyjs/wasm-edit": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.3.1", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "pkg-dir": "^5.0.0", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.0.3", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "enhanced-resolve": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", + "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "requires": { + "find-up": "^5.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "dev": true + } + } + }, + "webpack-node-externals": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-2.5.2.tgz", + "integrity": "sha512-aHdl/y2N7PW2Sx7K+r3AxpJO+aDMcYzMQd60Qxefq3+EwhewSbTBqNumOsCE1JsCUNoyfGj5465N0sSf6hc/5w==", + "dev": true + }, + "webpack-sources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", + "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.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==", + "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" + } + } + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "requires": { + "execa": "^4.0.2" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..cb9e18df --- /dev/null +++ b/package.json @@ -0,0 +1,148 @@ +{ + "name": "@staart/api", + "template": "staart/api", + "version": "4.0.9", + "description": "SaaS backend framework with users, payments, APIs, and more", + "repository": "git@github.com:staart/api.git", + "author": "Anand Chowdhary ", + "license": "MIT", + "scripts": { + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/src/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "npm run test:unit && npm run test:e2e", + "test:watch": "jest --watch --testTimeout 30000", + "test:cov": "jest --coverage --forceExit --testTimeout 30000", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand --testTimeout 30000", + "test:unit": "jest --forceExit --testTimeout 30000", + "test:e2e": "export TEST=true; npx ts-node tests/test-before.ts && jest --runInBand --config ./tests/jest-e2e.json --forceExit --testTimeout 30000" + }, + "dependencies": { + "@elastic/elasticsearch": "^7.10.0", + "@googlemaps/google-maps-services-js": "^3.1.14", + "@koj/config": "^1.2.11", + "@nestjs/common": "^7.6.5", + "@nestjs/config": "^0.6.1", + "@nestjs/core": "^7.6.5", + "@nestjs/passport": "^7.1.5", + "@nestjs/platform-express": "^7.6.5", + "@nestjs/schedule": "^0.4.1", + "@nestjs/swagger": "^4.7.9", + "@octokit/rest": "^18.0.12", + "@sentry/node": "^6.0.0", + "@sindresorhus/slugify": "^1.1.0", + "@slack/web-api": "^6.0.0", + "@staart/mustache-markdown": "^3.0.3", + "aws-elasticsearch-connector": "^9.0.1", + "aws-sdk": "^2.824.0", + "axios": "^0.21.1", + "bcrypt": "^5.0.0", + "cheerio": "^1.0.0-rc.5", + "circularbuffer": "^0.1.1", + "class-transformer": "^0.3.1", + "class-validator": "^0.12.2", + "cloudinary": "^1.23.0", + "crypto-random-string": "^3.3.0", + "deep-equal": "^2.0.5", + "dot-object": "^2.1.4", + "formdata-node": "^2.4.0", + "fs-extra": "^9.0.1", + "geolite2-redist": "^1.0.7", + "got": "^11.8.1", + "helmet": "^4.3.1", + "hibp": "^10.0.1", + "ip-anonymize": "^0.1.0", + "ip-range-check": "^0.2.0", + "jsonwebtoken": "^8.5.1", + "maxmind": "^4.3.1", + "mem": "^8.0.0", + "minimatch": "^3.0.4", + "nodemailer": "^6.4.17", + "normalize-email": "^1.1.1", + "nunjucks": "^3.2.2", + "otplib": "^12.0.1", + "p-queue": "^6.6.2", + "p-retry": "^4.2.0", + "passport": "^0.4.1", + "pidusage": "^2.0.21", + "puppeteer": "^5.5.0", + "qrcode": "^1.4.4", + "quick-lru": "^5.1.1", + "randomcolor": "^0.6.2", + "rate-limiter-flexible": "^2.1.16", + "reflect-metadata": "^0.1.13", + "request-ip": "^2.1.3", + "response-time": "^2.3.2", + "rimraf": "^3.0.2", + "rxjs": "^6.6.3", + "stripe": "^8.130.0", + "swagger-ui-express": "^4.1.6", + "twilio": "^3.54.2", + "ua-parser-js": "^0.7.23", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@nestjs/cli": "^7.5.4", + "@nestjs/schematics": "^7.2.6", + "@nestjs/testing": "^7.6.5", + "@prisma/cli": "^2.14.0", + "@prisma/client": "^2.14.0", + "@types/bcrypt": "^3.0.0", + "@types/cheerio": "^0.22.23", + "@types/deep-equal": "^1.0.1", + "@types/dot-object": "^2.1.2", + "@types/express": "^4.17.9", + "@types/fs-extra": "^9.0.6", + "@types/jest": "26.0.20", + "@types/jsonwebtoken": "^8.5.0", + "@types/minimatch": "^3.0.3", + "@types/node": "^14.14.20", + "@types/nodemailer": "^6.4.0", + "@types/nunjucks": "^3.1.3", + "@types/passport-local": "^1.0.33", + "@types/pidusage": "^2.0.1", + "@types/puppeteer": "^5.4.2", + "@types/qrcode": "^1.3.5", + "@types/randomcolor": "^0.5.5", + "@types/request-ip": "0.0.35", + "@types/response-time": "^2.3.4", + "@types/supertest": "^2.0.10", + "@types/ua-parser-js": "^0.7.35", + "@types/uuid": "^8.3.0", + "@typescript-eslint/eslint-plugin": "4.15.0", + "@typescript-eslint/parser": "4.15.2", + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "eslint": "7.20.0", + "eslint-config-prettier": "^7.1.0", + "eslint-plugin-import": "^2.22.1", + "jest": "26.6.3", + "prettier": "^2.2.1", + "supertest": "^6.0.1", + "ts-jest": "26.5.0", + "ts-loader": "^8.0.14", + "ts-node": "9.1.1", + "tsconfig-paths": "^3.9.0", + "typescript": "^4.1.3", + "utility-types": "^3.10.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 00000000..77ced665 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,289 @@ +generator client { + provider = "prisma-client-js" +} + +datasource mysql { + provider = "mysql" + url = env("DATABASE_URL") +} + +model User { + checkLocationOnLogin Boolean @default(false) + countryCode String @default("us") + createdAt DateTime @default(now()) + gender Gender @default(UNKNOWN) + id Int @id + name String + notificationEmail NotificationEmail @default(ACCOUNT) + password String? + prefersLanguage String @default("en-us") + prefersColorScheme PrefersColorScheme @default(NO_PREFERENCE) + prefersReducedMotion PrefersReducedMotion @default(NO_PREFERENCE) + prefersEmailId Int? + profilePictureUrl String @default("https://unavatar.now.sh/fallback.png") + role UserRole @default(USER) + timezone String @default("America/Los_Angeles") + twoFactorMethod MfaMethod @default(NONE) + twoFactorPhone String? + twoFactorSecret String? + attributes Json? + updatedAt DateTime @updatedAt + active Boolean @default(true) + prefersEmail Email? @relation("userPrefersEmail", fields: [prefersEmailId], references: [id]) + apiKeys ApiKey[] @relation("userApiKey") + approvedSubnets ApprovedSubnet[] @relation("userApprovedSubnet") + auditLogs AuditLog[] @relation("userAuditLog") + backupCodes BackupCode[] @relation("userBackupCode") + emails Email[] @relation("userEmail") + identities Identity[] @relation("userIdentity") + memberships Membership[] @relation("userMembership") + sessions Session[] @relation("userSession") + + @@index([prefersEmailId], name: "prefersEmailId") +} + +model Group { + autoJoinDomain Boolean @default(false) + createdAt DateTime @default(now()) + forceTwoFactor Boolean @default(false) + id Int @id + ipRestrictions String? + name String + onlyAllowDomain Boolean @default(false) + profilePictureUrl String @default("https://unavatar.now.sh/fallback.png") + attributes Json? + updatedAt DateTime @updatedAt + parentId Int? + parent Group? @relation("groupSubgroups", fields: [parentId], references: [id]) + apikeys ApiKey[] @relation("groupApiKey") + auditLogs AuditLog[] @relation("groupAuditLog") + domains Domain[] @relation("groupDomain") + subgroups Group[] @relation("groupSubgroups") + memberships Membership[] @relation("groupMembership") + webhooks Webhook[] @relation("groupWebhook") + + @@index([parentId], name: "parentId") +} + +model Email { + createdAt DateTime @default(now()) + email String @unique + emailSafe String @unique + id Int @id @default(autoincrement()) + isVerified Boolean @default(false) + updatedAt DateTime @updatedAt + userId Int + user User @relation("userEmail", fields: [userId], references: [id]) + users User[] @relation("userPrefersEmail") + + @@index([userId], name: "userId") +} + +model ApiKey { + createdAt DateTime @default(now()) + description String? + id Int @id @default(autoincrement()) + ipRestrictions Json? + apiKey String @unique + name String? + groupId Int? + referrerRestrictions Json? + scopes Json? + updatedAt DateTime @updatedAt + userId Int? + auditLogs AuditLog[] @relation("apiKeyAuditLog") + group Group? @relation("groupApiKey", fields: [groupId], references: [id]) + user User? @relation("userApiKey", fields: [userId], references: [id]) + + @@index([groupId], name: "groupId") + @@index([userId], name: "userId") +} + +model ApprovedSubnet { + createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + subnet String + city String? + region String? + timezone String? + countryCode String? + updatedAt DateTime @updatedAt + userId Int + user User @relation("userApprovedSubnet", fields: [userId], references: [id]) + + @@index([userId], name: "userId") +} + +model BackupCode { + id Int @id @default(autoincrement()) + code String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + isUsed Boolean @default(false) + userId Int + user User @relation("userBackupCode", fields: [userId], references: [id]) + + @@index([userId], name: "userId") +} + +model CouponCode { + id Int @id @default(autoincrement()) + code String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + expiresAt DateTime? + maxUses Int @default(1000) + usedCount Int @default(0) + teamRestrictions String? + amount Float @default(0.00) + currency String + description String? +} + +model Domain { + createdAt DateTime @default(now()) + domain String + id Int @id @default(autoincrement()) + isVerified Boolean @default(false) + groupId Int + updatedAt DateTime @updatedAt + verificationCode String + group Group @relation("groupDomain", fields: [groupId], references: [id]) + + @@index([groupId], name: "groupId") +} + +model Identity { + createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + loginName String + type IdentityType + updatedAt DateTime @updatedAt + userId Int + user User @relation("userIdentity", fields: [userId], references: [id]) + + @@index([userId], name: "userId") +} + +model Membership { + createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + groupId Int + role MembershipRole @default(MEMBER) + updatedAt DateTime @updatedAt + userId Int + group Group @relation("groupMembership", fields: [groupId], references: [id]) + user User @relation("userMembership", fields: [userId], references: [id]) + + @@index([groupId], name: "groupId") + @@index([userId], name: "userId") +} + +model Session { + createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + ipAddress String + token String + updatedAt DateTime @updatedAt + userAgent String? + city String? + region String? + timezone String? + countryCode String? + browser String? + operatingSystem String? + userId Int + user User @relation("userSession", fields: [userId], references: [id]) + + @@index([userId], name: "userId") +} + +model Webhook { + contentType String @default("application/json") + createdAt DateTime @default(now()) + event String + id Int @id @default(autoincrement()) + isActive Boolean @default(false) + lastFiredAt DateTime? + groupId Int + secret String? + updatedAt DateTime @updatedAt + url String + group Group @relation("groupWebhook", fields: [groupId], references: [id]) + + @@index([groupId], name: "groupId") +} + +model AuditLog { + createdAt DateTime @default(now()) + event String + rawEvent String + id Int @id @default(autoincrement()) + groupId Int? + updatedAt DateTime @updatedAt + userId Int? + apiKeyId Int? + ipAddress String? + userAgent String? + city String? + region String? + timezone String? + countryCode String? + browser String? + operatingSystem String? + group Group? @relation("groupAuditLog", fields: [groupId], references: [id]) + user User? @relation("userAuditLog", fields: [userId], references: [id]) + apiKey ApiKey? @relation("apiKeyAuditLog", fields: [apiKeyId], references: [id]) + + @@index([apiKeyId], name: "apiKeyId") + @@index([groupId], name: "groupId") + @@index([userId], name: "userId") +} + +enum Gender { + MALE + FEMALE + NONBINARY + UNKNOWN +} + +enum NotificationEmail { + ACCOUNT + UPDATES + PROMOTIONS +} + +enum PrefersColorScheme { + NO_PREFERENCE + LIGHT + DARK +} + +enum PrefersReducedMotion { + NO_PREFERENCE + REDUCE +} + +enum UserRole { + SUDO + USER +} + +enum MfaMethod { + NONE + SMS + TOTP + EMAIL +} + +enum MembershipRole { + OWNER + ADMIN + MEMBER +} + +enum IdentityType { + GOOGLE + APPLE + SLACK +} diff --git a/release.config.js b/release.config.js new file mode 100644 index 00000000..2aba67b1 --- /dev/null +++ b/release.config.js @@ -0,0 +1 @@ +module.exports = require("@koj/config").releaseMaster; diff --git a/src/app.module.ts b/src/app.module.ts new file mode 100644 index 00000000..9ec4d54a --- /dev/null +++ b/src/app.module.ts @@ -0,0 +1,117 @@ +import { + MiddlewareConsumer, + Module, + NestModule, + RequestMethod, +} from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core'; +import { ScheduleModule } from '@nestjs/schedule'; +import configuration from './config/configuration'; +import { AuditLogger } from './interceptors/audit-log.interceptor'; +import { RateLimitInterceptor } from './interceptors/rate-limit.interceptor'; +import { ApiLoggerMiddleware } from './middleware/api-logger.middleware'; +import { JsonBodyMiddleware } from './middleware/json-body.middleware'; +import { RawBodyMiddleware } from './middleware/raw-body.middleware'; +import { ApiKeysModule } from './modules/api-keys/api-keys.module'; +import { ApprovedSubnetsModule } from './modules/approved-subnets/approved-subnets.module'; +import { AuditLogsModule } from './modules/audit-logs/audit-logs.module'; +import { AuthModule } from './modules/auth/auth.module'; +import { ScopesGuard } from './modules/auth/scope.guard'; +import { StaartAuthGuard } from './modules/auth/staart-auth.guard'; +import { DomainsModule } from './modules/domains/domains.module'; +import { EmailsModule } from './modules/emails/emails.module'; +import { GroupsModule } from './modules/groups/groups.module'; +import { MembershipsModule } from './modules/memberships/memberships.module'; +import { MultiFactorAuthenticationModule } from './modules/multi-factor-authentication/multi-factor-authentication.module'; +import { SessionsModule } from './modules/sessions/sessions.module'; +import { StripeModule } from './modules/stripe/stripe.module'; +import { UsersModule } from './modules/users/users.module'; +import { WebhooksModule } from './modules/webhooks/webhooks.module'; +import { CloudinaryModule } from './providers/cloudinary/cloudinary.module'; +import { DnsModule } from './providers/dns/dns.module'; +import { ElasticSearchModule } from './providers/elasticsearch/elasticsearch.module'; +import { GeolocationModule } from './providers/geolocation/geolocation.module'; +import { GitHubModule } from './providers/github/github.module'; +import { GoogleMapsModule } from './providers/google-maps/google-maps.module'; +import { MailModule } from './providers/mail/mail.module'; +import { PuppeteerModule } from './providers/puppeteer/puppeteer.module'; +import { PrismaModule } from './providers/prisma/prisma.module'; +import { S3Module } from './providers/s3/s3.module'; +import { SlackModule } from './providers/slack/slack.module'; +import { TasksModule } from './providers/tasks/tasks.module'; +import { HttpExceptionFilter } from './filters/http-exception.filter'; +import { MetricsModule } from './modules/metrics/metrics.module'; +import { MetaModule } from './modules/meta/meta.module'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + load: [configuration], + }), + ScheduleModule.forRoot(), + PrismaModule, + TasksModule, + UsersModule, + AuthModule, + MailModule, + SessionsModule, + EmailsModule, + GroupsModule, + MultiFactorAuthenticationModule, + ApiKeysModule, + ApprovedSubnetsModule, + DomainsModule, + DnsModule, + GeolocationModule, + MembershipsModule, + StripeModule, + AuditLogsModule, + WebhooksModule, + ElasticSearchModule, + SlackModule, + S3Module, + CloudinaryModule, + GitHubModule, + GoogleMapsModule, + PuppeteerModule, + MetricsModule, + MetaModule, + ], + providers: [ + { + provide: APP_FILTER, + useClass: HttpExceptionFilter, + }, + { + provide: APP_INTERCEPTOR, + useClass: RateLimitInterceptor, + }, + { + provide: APP_GUARD, + useClass: StaartAuthGuard, + }, + { + provide: APP_GUARD, + useClass: ScopesGuard, + }, + { + provide: APP_INTERCEPTOR, + useClass: AuditLogger, + }, + ], +}) +export class AppModule implements NestModule { + public configure(consumer: MiddlewareConsumer): void { + consumer + .apply(RawBodyMiddleware) + .forRoutes({ + path: '/webhooks/stripe', + method: RequestMethod.POST, + }) + .apply(JsonBodyMiddleware) + .forRoutes('*') + .apply(ApiLoggerMiddleware) + .forRoutes('*'); + } +} diff --git a/src/config/configuration.interface.ts b/src/config/configuration.interface.ts new file mode 100644 index 00000000..52c8cd6e --- /dev/null +++ b/src/config/configuration.interface.ts @@ -0,0 +1,124 @@ +import { ApiKeyAuth, BasicAuth } from '@elastic/elasticsearch/lib/pool'; +import Stripe from 'stripe'; + +export interface Configuration { + frontendUrl: string; + + meta: { + appName: string; + domainVerificationFile: string; + }; + + caching: { + geolocationLruSize: number; + apiKeyLruSize: number; + }; + + rateLimit: { + public: { points: number; duration: number }; + authenticated: { points: number; duration: number }; + apiKey: { points: number; duration: number }; + }; + + security: { + saltRounds: number; + jwtSecret: string; + totpWindowPast: number; + totpWindowFuture: number; + mfaTokenExpiry: string; + mergeUsersTokenExpiry: string; + accessTokenExpiry: string; + passwordPwnedCheck: boolean; + unusedRefreshTokenExpiryDays: number; + inactiveUserDeleteDays: number; + }; + + email: { + name: string; + from: string; + retries: number; + ses?: { + accessKeyId: string; + secretAccessKey: string; + region: string; + }; + transport?: { + host: string; + port: number; + secure: boolean; + auth: { + user: string; + pass: string; + }; + }; + }; + + elasticSearch: { + node: string; + retries: number; + auth?: BasicAuth | ApiKeyAuth; + aws?: { + accessKeyId: string; + secretAccessKey: string; + region: string; + }; + }; + + webhooks: { + retries: number; + }; + + sms: { + retries: number; + twilioAccountSid: string; + twilioAuthToken: string; + }; + + payments: { + stripeApiKey: string; + stripeProductId: string; + stripeEndpointSecret: string; + paymentMethodTypes: Array; + }; + + tracking: { + mode: 'all' | 'api-key' | 'user' | 'api-key-or-user'; + index: string; + deleteOldLogs: boolean; + deleteOldLogsDays: number; + }; + + slack: { + token: string; + slackApiUrl?: string; + rejectRateLimitedCalls?: boolean; + retries: number; + }; + + s3: { + accessKeyId: string; + secretAccessKey: string; + region: string; + profilePictureBucket?: string; + profilePictureCdnHostname?: string; + }; + + cloudinary: { + cloudName: string; + apiKey: string; + apiSecret: string; + }; + + github: { + auth: string; + userAgent?: string; + }; + + googleMaps: { + apiKey: string; + }; + + gravatar: { + enabled: boolean; + }; +} diff --git a/src/config/configuration.ts b/src/config/configuration.ts new file mode 100644 index 00000000..5aa81f00 --- /dev/null +++ b/src/config/configuration.ts @@ -0,0 +1,149 @@ +import { ConfigFactory } from '@nestjs/config/dist/interfaces'; +import { config } from 'dotenv'; +import dotenvExpand from 'dotenv-expand'; +import { Configuration } from './configuration.interface'; +dotenvExpand(config()); + +const int = (val: string | undefined, num: number): number => + val ? (isNaN(parseInt(val)) ? num : parseInt(val)) : num; +const bool = (val: string | undefined, bool: boolean): boolean => + val == null ? bool : val == 'true'; + +const configuration: Configuration = { + frontendUrl: process.env.FRONTEND_URL ?? 'http://localhost:3000', + meta: { + appName: process.env.APP_NAME ?? 'Staart', + domainVerificationFile: + process.env.DOMAIN_VERIFICATION_FILE ?? 'staart-verify.txt', + }, + rateLimit: { + public: { + points: int(process.env.RATE_LIMIT_PUBLIC_POINTS, 250), + duration: int(process.env.RATE_LIMIT_PUBLIC_DURATION, 3600), + }, + authenticated: { + points: int(process.env.RATE_LIMIT_AUTHENTICATED_POINTS, 5000), + duration: int(process.env.RATE_LIMIT_AUTHENTICATED_DURATION, 3600), + }, + apiKey: { + points: int(process.env.RATE_LIMIT_API_KEY_POINTS, 10000), + duration: int(process.env.RATE_LIMIT_API_KEY_DURATION, 3600), + }, + }, + caching: { + geolocationLruSize: int(process.env.GEOLOCATION_LRU_SIZE, 100), + apiKeyLruSize: int(process.env.API_KEY_LRU_SIZE, 100), + }, + security: { + saltRounds: int(process.env.SALT_ROUNDS, 10), + jwtSecret: process.env.JWT_SECRET ?? 'staart', + totpWindowPast: int(process.env.TOTP_WINDOW_PAST, 1), + totpWindowFuture: int(process.env.TOTP_WINDOW_FUTURE, 0), + mfaTokenExpiry: process.env.MFA_TOKEN_EXPIRY ?? '10m', + mergeUsersTokenExpiry: process.env.MERGE_USERS_TOKEN_EXPIRY ?? '30m', + accessTokenExpiry: process.env.ACCESS_TOKEN_EXPIRY ?? '1h', + passwordPwnedCheck: bool(process.env.PASSWORD_PWNED_CHECK, true), + unusedRefreshTokenExpiryDays: int(process.env.DELETE_EXPIRED_SESSIONS, 30), + inactiveUserDeleteDays: int(process.env.INACTIVE_USER_DELETE_DAYS, 30), + }, + elasticSearch: { + node: process.env.ELASTICSEARCH_NODE, + retries: int(process.env.ELASTICSEARCH_FAIL_RETRIES, 3), + auth: process.env.ELASTICSEARCH_AUTH_USERNAME + ? { + username: process.env.ELASTICSEARCH_AUTH_USERNAME, + password: process.env.ELASTICSEARCH_AUTH_PASSWORD, + } + : process.env.ELASTICSEARCH_AUTH_API_KEY + ? process.env.ELASTICSEARCH_AUTH_API_KEY_ID + ? { + apiKey: { + api_key: process.env.ELASTICSEARCH_AUTH_API_KEY, + id: process.env.ELASTICSEARCH_AUTH_API_KEY_ID, + }, + } + : { apiKey: process.env.ELASTICSEARCH_AUTH_API_KEY } + : undefined, + aws: { + accessKeyId: process.env.ELASTICSEARCH_AWS_ACCESS_KEY_ID ?? '', + secretAccessKey: process.env.ELASTICSEARCH_AWS_SECRET_ACCESS_KEY ?? '', + region: process.env.ELASTICSEARCH_AWS_REGION ?? '', + }, + }, + email: { + name: process.env.EMAIL_NAME ?? 'Staart', + from: process.env.EMAIL_FROM ?? '', + retries: int(process.env.EMAIL_FAIL_RETRIES, 3), + ses: { + accessKeyId: process.env.EMAIL_SES_ACCESS_KEY_ID ?? '', + secretAccessKey: process.env.EMAIL_SES_SECRET_ACCESS_KEY ?? '', + region: process.env.EMAIL_SES_REGION ?? '', + }, + transport: { + host: process.env.EMAIL_HOST ?? '', + port: int(process.env.EMAIL_PORT, 587), + secure: bool(process.env.EMAIL_SECURE, false), + auth: { + user: process.env.EMAIL_USER ?? process.env.EMAIL_FROM ?? '', + pass: process.env.EMAIL_PASSWORD ?? '', + }, + }, + }, + webhooks: { + retries: int(process.env.WEBHOOK_FAIL_RETRIES, 3), + }, + sms: { + retries: int(process.env.SMS_FAIL_RETRIES, 3), + twilioAccountSid: process.env.TWILIO_ACCOUNT_SID ?? '', + twilioAuthToken: process.env.TWILIO_AUTH_TOKEN ?? '', + }, + payments: { + stripeApiKey: process.env.STRIPE_API_KEY ?? '', + stripeProductId: process.env.STRIPE_PRODUCT_ID ?? '', + stripeEndpointSecret: process.env.STRIPE_ENDPOINT_SECRET ?? '', + paymentMethodTypes: ['card'], + }, + tracking: { + mode: + (process.env.TRACKING_MODE as Configuration['tracking']['mode']) ?? + 'api-key', + index: process.env.TRACKING_INDEX ?? 'staart-logs', + deleteOldLogs: bool(process.env.TRACKING_DELETE_OLD_LOGS, true), + deleteOldLogsDays: int(process.env.TRACKING_DELETE_OLD_LOGS_DAYS, 90), + }, + slack: { + token: process.env.SLACK_TOKEN ?? '', + slackApiUrl: process.env.SLACK_API_URL, + rejectRateLimitedCalls: bool( + process.env.SLACK_REJECT_RATE_LIMITED_CALLS, + false, + ), + retries: int(process.env.SLACK_FAIL_RETRIES, 3), + }, + s3: { + accessKeyId: process.env.AWS_S3_ACCESS_KEY ?? '', + secretAccessKey: process.env.AWS_S3_SECRET_KEY ?? '', + region: process.env.AWS_S3_REGION ?? '', + profilePictureBucket: process.env.AWS_S3_PROFILE_PICTURE_BUCKET ?? '', + profilePictureCdnHostname: + process.env.AWS_S3_PROFILE_PICTURE_CDN_HOST_NAME ?? '', + }, + cloudinary: { + cloudName: process.env.CLOUDINARY_CLOUD_NAME ?? '', + apiKey: process.env.CLOUDINARY_API_KEY ?? '', + apiSecret: process.env.CLOUDINARY_API_SECRET ?? '', + }, + github: { + auth: process.env.GITHUB_AUTH, + userAgent: process.env.GITHUB_USER_AGENT, + }, + googleMaps: { + apiKey: process.env.GOOGLE_MAPS_API_KEY, + }, + gravatar: { + enabled: bool(process.env.PASSWORD_PWNED_CHECK, true), + }, +}; + +const configFunction: ConfigFactory = () => configuration; +export default configFunction; diff --git a/src/errors/errors.constants.ts b/src/errors/errors.constants.ts new file mode 100644 index 00000000..c8e39b31 --- /dev/null +++ b/src/errors/errors.constants.ts @@ -0,0 +1,60 @@ +export const USER_NOT_FOUND = '404001: User not found'; +export const GROUP_NOT_FOUND = '404002: Group not found'; +export const SESSION_NOT_FOUND = '404003: Session not found'; +export const EMAIL_NOT_FOUND = '404004: Email not found'; +export const API_KEY_NOT_FOUND = '404005: API key not found'; +export const APPROVED_SUBNET_NOT_FOUND = '404006: Approved subnet not found'; +export const AUDIT_LOG_NOT_FOUND = '404007: Audit log not found'; +export const DOMAIN_NOT_FOUND = '404008: Domain not found'; +export const MEMBERSHIP_NOT_FOUND = '404009: Membership not found'; +export const BILLING_NOT_FOUND = '404010: Billing not found'; +export const CUSTOMER_NOT_FOUND = '404011: Customer not found'; +export const INVOICE_NOT_FOUND = '404012: Invoice not found'; +export const SUBSCRIPTION_NOT_FOUND = '404013: Subscription not found'; +export const SOURCE_NOT_FOUND = '404014: Source not found'; +export const WEBHOOK_NOT_FOUND = '404015: Webhook not found'; + +export const UNAUTHORIZED_RESOURCE = '401000: Insufficient permission'; +export const INVALID_CREDENTIALS = '401001: Invalid credentials'; +export const INVALID_MFA_CODE = '401002: Invalid one-time code'; +export const INVALID_TOKEN = '401003: Invalid token'; +export const UNVERIFIED_EMAIL = '401004: Email is not verified'; +export const UNVERIFIED_LOCATION = '401005: Location is not verified'; +export const MFA_BACKUP_CODE_USED = '401007: Backup code is already used'; + +export const NO_TOKEN_PROVIDED = '400001: No token provided'; +export const DOMAIN_NOT_VERIFIED = '400002: Domain not verified'; +export const MFA_PHONE_NOT_FOUND = '400003: Phone number not found'; +export const MFA_PHONE_OR_TOKEN_REQUIRED = + '400004: Phone number or token is required'; +export const MFA_NOT_ENABLED = + '400005: Multi-factor authentication is not enabled'; +export const NO_EMAILS = '400006: User has no email attached to it'; +export const CURRENT_PASSWORD_REQUIRED = '400007: Current password is required'; +export const COMPROMISED_PASSWORD = + '400008: This password has been compromised in a data breach.'; +export const CANNOT_DELETE_SOLE_MEMBER = + '400009: Cannot remove the only member'; +export const CANNOT_DELETE_SOLE_OWNER = '400010: Cannot remove the only owner'; +export const ORDER_BY_ASC_DESC = '400011: Invalid sorting order'; +export const ORDER_BY_FORMAT = '400012: Invalid ordering format'; +export const WHERE_PIPE_FORMAT = '400013: Invalid query format'; +export const OPTIONAL_INT_PIPE_NUMBER = '400014: $key should be a number'; +export const CURSOR_PIPE_FORMAT = '400015: Invalid cursor format'; +export const EMAIL_DELETE_PRIMARY = '400016: Cannot delete primary email'; +export const CANNOT_UPDATE_ROLE_SOLE_OWNER = + '400017: Cannot change the role of the only owner'; +export const INVALID_DOMAIN = '400018: Invalid domain'; +export const SELECT_INCLUDE_PIPE_FORMAT = '400019: Invalid query format'; +export const FILE_TOO_LARGE = '400022: Uploaded file is too large'; + +export const EMAIL_USER_CONFLICT = + '409001: User with this email already exists'; +export const EMAIL_VERIFIED_CONFLICT = '409002: This email is already verified'; +export const BILLING_ACCOUNT_CREATED_CONFLICT = + '409003: Billing account is already created'; +export const MFA_ENABLED_CONFLICT = + '409004: Multi-factor authentication is already enabled'; +export const MERGE_USER_CONFLICT = '409005: Cannot merge the same user'; + +export const RATE_LIMIT_EXCEEDED = '429000: Rate limit exceeded'; diff --git a/src/filters/http-exception.filter.ts b/src/filters/http-exception.filter.ts new file mode 100644 index 00000000..73ea3ead --- /dev/null +++ b/src/filters/http-exception.filter.ts @@ -0,0 +1,36 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import * as Sentry from '@sentry/node'; +import { Response } from 'express'; + +// If you have a Sentry DSN, add it here for error tracking and uncomment the Sentry lines (12, 28) +// Sentry.init({ dsn: '' }); + +@Catch() +export class HttpExceptionFilter implements ExceptionFilter { + catch(exception: unknown, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const status = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + if (exception instanceof HttpException) { + return response.status(status).json(exception.getResponse()); + } + + // Sentry.captureException(exception); + + response.status(status).json({ + statusCode: status, + message: 'We got an error in processing this request', + error: 'Internal server error', + }); + } +} diff --git a/src/helpers/interfaces.ts b/src/helpers/interfaces.ts new file mode 100644 index 00000000..e77ae150 --- /dev/null +++ b/src/helpers/interfaces.ts @@ -0,0 +1,8 @@ +export type Files = { + fieldname: string; + originalname: string; + encoding: '7bit'; + mimetype: string; + buffer: Buffer; + size: number; +}[]; diff --git a/src/helpers/parse-object-literal.ts b/src/helpers/parse-object-literal.ts new file mode 100644 index 00000000..11200619 --- /dev/null +++ b/src/helpers/parse-object-literal.ts @@ -0,0 +1,81 @@ +/** + * Parse a string like "a: 1, b: 2" to { a: 1, b: 2 } + * @param objectLiteralString - String to parse + * @source https://github.com/mbest/js-object-literal-parse + */ +export const parseObjectLiteral = ( + objectLiteralString: string, +): [string, string | undefined][] => { + const stringDouble = '"(?:[^"\\\\]|\\\\.)*"'; + const stringSingle = "'(?:[^'\\\\]|\\\\.)*'"; + const stringRegexp = '/(?:[^/\\\\]|\\\\.)*/w*'; + const specials = ',"\'{}()/:[\\]'; + const everyThingElse = '[^\\s:,/][^' + specials + ']*[^\\s' + specials + ']'; + const oneNotSpace = '[^\\s]'; + const token = RegExp( + stringDouble + + '|' + + stringSingle + + '|' + + stringRegexp + + '|' + + everyThingElse + + '|' + + oneNotSpace, + 'g', + ); + const divisionLookBehind = /[\])"'A-Za-z0-9_$]+$/; + const keywordRegexLookBehind: Record = { + in: 1, + return: 1, + typeof: 1, + }; + let str = objectLiteralString.trim(); + if (str.charCodeAt(0) === 123) str = str.slice(1, -1); + const result: [string, string | undefined][] = []; + let toks = str.match(token) as RegExpMatchArray; + if (!toks) return result; + let key: string | undefined = undefined; + let values = []; + let depth = 0; + toks.push(','); + for (let i = 0, tok: string; (tok = toks[i]); ++i) { + const c = tok.charCodeAt(0); + if (c === 44) { + if (depth <= 0) { + if (!key && values.length === 1) { + key = values.pop(); + } + if (key) + result.push([key, values.length ? values.join('') : undefined]); + key = undefined; + values = []; + depth = 0; + continue; + } + } else if (c === 58) { + if (!depth && !key && values.length === 1) { + key = values.pop(); + continue; + } + } else if (c === 47 && i && tok.length > 1) { + const match = toks[i - 1].match(divisionLookBehind); + if (match && !keywordRegexLookBehind[match[0]]) { + str = str.substr(str.indexOf(tok) + 1); + const result = str.match(token); + if (result) toks = result; + toks.push(','); + i = -1; + tok = '/'; + } + } else if (c === 40 || c === 123 || c === 91) { + ++depth; + } else if (c === 41 || c === 125 || c === 93) { + --depth; + } else if (!key && !values.length && (c === 34 || c === 39)) { + tok = tok.slice(1, -1); + } + values.push(tok); + } + return result; +}; diff --git a/src/helpers/safe-email.ts b/src/helpers/safe-email.ts new file mode 100644 index 00000000..d598f0af --- /dev/null +++ b/src/helpers/safe-email.ts @@ -0,0 +1,9 @@ +import normalize from 'normalize-email'; + +/** + * Converts an email address to a unqiue, safe email + * @param email - Valid email address + */ +export const safeEmail = (input: string) => { + return normalize(input.toLowerCase().trim()); +}; diff --git a/src/helpers/scopes.ts b/src/helpers/scopes.ts new file mode 100644 index 00000000..386fd8c7 --- /dev/null +++ b/src/helpers/scopes.ts @@ -0,0 +1,107 @@ +/** Scopes for a user */ +export const userScopes = { + 'user-{userId}:write-api-key-*': 'Create and update API keys', + 'user-{userId}:read-api-key-*': 'Read API keys', + 'user-{userId}:delete-api-key-*': 'Delete API keys', + 'user-{userId}:read-api-key-logs-*': 'Read API key logs', + 'user-{userId}:read-approved-subnet-*': 'Read approved subnets', + 'user-{userId}:delete-approved-subnet-*': 'Unapproved subnet', + 'user-{userId}:write-email-*': 'Create and update emails', + 'user-{userId}:read-email-*': 'Read emails', + 'user-{userId}:delete-email-*': 'Delete emails', + 'user-{userId}:write-membership-*': 'Create groups', + 'user-{userId}:read-membership-*': 'Read memberships', + 'user-{userId}:delete-membership-*': 'Delete memberships', + 'user-{userId}:delete-mfa-*': 'Disable MFA', + 'user-{userId}:write-mfa-regenerate': 'Regenerate MFA backup codes', + 'user-{userId}:write-mfa-totp': 'Enable TOTP-based MFA', + 'user-{userId}:write-mfa-sms': 'Enable SMS-based MFA', + 'user-{userId}:write-mfa-email': 'Enable email-based MFA', + 'user-{userId}:read-session-*': 'Read sessions', + 'user-{userId}:delete-session-*': 'Log out of sessions', + 'user-{userId}:read-info': 'Read user details', + 'user-{userId}:write-info': 'Update user details', + 'user-{userId}:deactivate': 'Delete user account', + 'user-{userId}:merge': 'Merge two users', +}; + +/** Scopes for a group owner */ +export const groupOwnerScopes = { + 'group-{groupId}:write-api-key-*': 'Create and update API keys', + 'group-{groupId}:read-api-key-*': 'Read API keys', + 'group-{groupId}:delete-api-key-*': 'Delete API keys', + 'group-{groupId}:read-api-key-logs-*': 'Read API key logs', + 'group-{groupId}:read-audit-log-*': 'Read audit log', + 'group-{groupId}:write-domain-*': 'Create and update domains', + 'group-{groupId}:read-domain-*': 'Read domains', + 'group-{groupId}:delete-domain-*': 'Delete domains', + 'group-{groupId}:read-info': 'Read apartment details', + 'group-{groupId}:write-info': 'Update apartment details', + 'group-{groupId}:delete': 'Delete apartment', + 'group-{groupId}:write-membership-*': 'Create and update memberships', + 'group-{groupId}:read-membership-*': 'Read memberships', + 'group-{groupId}:delete-membership-*': 'Delete memberships', + 'group-{groupId}:write-billing': 'Create and update billing details', + 'group-{groupId}:read-billing': 'Read billing details', + 'group-{groupId}:delete-billing': 'Delete billing details', + 'group-{groupId}:read-invoice-*': 'Read invoices', + 'group-{groupId}:write-source-*': 'Create and update payment methods', + 'group-{groupId}:read-source-*': 'Read payment methods', + 'group-{groupId}:delete-source-*': 'Delete payment methods', + 'group-{groupId}:write-subscription-*': 'Create and update subscriptions', + 'group-{groupId}:read-subscription-*': 'Read subscriptions', + 'group-{groupId}:delete-subscription-*': 'Delete subscriptions', + 'group-{groupId}:write-webhook-*': 'Create and update webhooks', + 'group-{groupId}:read-webhook-*': 'Read webhooks', + 'group-{groupId}:delete-webhook-*': 'Delete webhooks', +}; + +/** + * Scopes for a group admin + * Admins can do everything except deleting the group or removing members + */ +export const groupAdminScopes = { + 'group-{groupId}:write-api-key-*': 'Create and update API keys', + 'group-{groupId}:read-api-key-*': 'Read API keys', + 'group-{groupId}:delete-api-key-*': 'Delete API keys', + 'group-{groupId}:read-api-key-logs-*': 'Read API key logs', + 'group-{groupId}:read-audit-log-*': 'Read audit log', + 'group-{groupId}:write-domain-*': 'Create and update domains', + 'group-{groupId}:read-domain-*': 'Read domains', + 'group-{groupId}:delete-domain-*': 'Delete domains', + 'group-{groupId}:read-info': 'Read apartment details', + 'group-{groupId}:write-info': 'Update apartment details', + 'group-{groupId}:write-membership-*': 'Create and update memberships', + 'group-{groupId}:read-membership-*': 'Read memberships', + 'group-{groupId}:write-billing': 'Create and update billing details', + 'group-{groupId}:read-billing': 'Read billing details', + 'group-{groupId}:delete-billing': 'Delete billing details', + 'group-{groupId}:read-invoice-*': 'Read invoices', + 'group-{groupId}:write-source-*': 'Create and update payment methods', + 'group-{groupId}:read-source-*': 'Read payment methods', + 'group-{groupId}:delete-source-*': 'Delete payment methods', + 'group-{groupId}:write-subscription-*': 'Create and update subscriptions', + 'group-{groupId}:read-subscription-*': 'Read subscriptions', + 'group-{groupId}:delete-subscription-*': 'Delete subscriptions', + 'group-{groupId}:write-webhook-*': 'Create and update webhooks', + 'group-{groupId}:read-webhook-*': 'Read webhooks', + 'group-{groupId}:delete-webhook-*': 'Delete webhooks', +}; + +/** + * Scopes for a group member + * Members have readonly access + */ +export const groupMemberScopes = { + 'group-{groupId}:read-api-key-*': 'Read API keys', + 'group-{groupId}:read-api-key-logs-*': 'Read API key logs', + 'group-{groupId}:read-audit-log-*': 'Read audit log', + 'group-{groupId}:read-domain-*': 'Read domains', + 'group-{groupId}:read-info': 'Read apartment details', + 'group-{groupId}:read-membership-*': 'Read memberships', + 'group-{groupId}:read-billing': 'Read billing details', + 'group-{groupId}:read-invoice-*': 'Read invoices', + 'group-{groupId}:read-source-*': 'Read payment methods', + 'group-{groupId}:read-subscription-*': 'Read subscriptions', + 'group-{groupId}:read-webhook-*': 'Read webhooks', +}; diff --git a/src/interceptors/audit-log.interceptor.ts b/src/interceptors/audit-log.interceptor.ts new file mode 100644 index 00000000..56ecd9ee --- /dev/null +++ b/src/interceptors/audit-log.interceptor.ts @@ -0,0 +1,85 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + Logger, + NestInterceptor, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import type { Prisma } from '@prisma/client'; +import { getClientIp } from 'request-ip'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import UAParser from 'ua-parser-js'; +import { STAART_AUDIT_LOG_DATA } from '../modules/audit-logs/audit-log.constants'; +import { UserRequest } from '../modules/auth/auth.interface'; +import { WebhooksService } from '../modules/webhooks/webhooks.service'; +import { GeolocationService } from '../providers/geolocation/geolocation.service'; +import { PrismaService } from '../providers/prisma/prisma.service'; + +@Injectable() +export class AuditLogger implements NestInterceptor { + logger = new Logger(AuditLogger.name); + + constructor( + private readonly reflector: Reflector, + private prisma: PrismaService, + private geolocationService: GeolocationService, + private webhooksService: WebhooksService, + ) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + let auditLog = this.reflector.get( + STAART_AUDIT_LOG_DATA, + context.getHandler(), + ); + return next.handle().pipe( + tap(() => { + (async () => { + if (auditLog) { + if (typeof auditLog === 'string') auditLog = [auditLog]; + const request = context.switchToHttp().getRequest() as UserRequest; + const groupId = parseInt(request.params.groupId); + const ip = getClientIp(request); + const location = await this.geolocationService.getLocation(ip); + const userAgent = request.get('user-agent'); + const ua = new UAParser(userAgent); + for await (const rawEvent of auditLog) { + let event = rawEvent; + if (request.user.id && request.user.type === 'user') + event = event.replace('{userId}', request.user.id.toString()); + if (groupId) + event = event.replace('{groupId}', groupId.toString()); + const data: Prisma.AuditLogCreateInput = { + event, + rawEvent, + city: location?.city?.names?.en, + region: location?.subdivisions?.pop()?.names?.en, + timezone: location?.location?.time_zone, + countryCode: location?.country?.iso_code, + userAgent, + browser: + `${ua.getBrowser().name ?? ''} ${ + ua.getBrowser().version ?? '' + }`.trim() || undefined, + operatingSystem: + `${ua.getOS().name ?? ''} ${ua.getOS().version ?? ''}` + .replace('Mac OS', 'macOS') + .trim() || undefined, + }; + if (request.user.id && request.user.type === 'user') + data.user = { connect: { id: request.user.id } }; + if (request.user.id && request.user.type === 'api-key') + data.apiKey = { connect: { id: request.user.id } }; + if (groupId) data.group = { connect: { id: groupId } }; + await this.prisma.auditLog.create({ data }); + if (groupId) this.webhooksService.triggerWebhook(groupId, event); + } + } + })() + .then(() => {}) + .catch((err) => this.logger.error('Unable to save audit log', err)); + }), + ); + } +} diff --git a/src/interceptors/rate-limit.interceptor.ts b/src/interceptors/rate-limit.interceptor.ts new file mode 100644 index 00000000..8282bcf2 --- /dev/null +++ b/src/interceptors/rate-limit.interceptor.ts @@ -0,0 +1,72 @@ +import { + CallHandler, + ExecutionContext, + HttpException, + HttpStatus, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Reflector } from '@nestjs/core'; +import { RateLimiterMemory } from 'rate-limiter-flexible'; +import { getClientIp } from 'request-ip'; +import { Observable } from 'rxjs'; +import { Configuration } from '../config/configuration.interface'; +import { RATE_LIMIT_EXCEEDED } from '../errors/errors.constants'; +import { UserRequest } from '../modules/auth/auth.interface'; + +@Injectable() +export class RateLimitInterceptor implements NestInterceptor { + private rateLimiterPublic = new RateLimiterMemory( + this.configService.get( + 'rateLimit.public', + ), + ); + private rateLimiterAuthenticated = new RateLimiterMemory( + this.configService.get( + 'rateLimit.authenticated', + ), + ); + private rateLimiterApiKey = new RateLimiterMemory( + this.configService.get( + 'rateLimit.apiKey', + ), + ); + + constructor( + private readonly reflector: Reflector, + private configService: ConfigService, + ) {} + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const points = + this.reflector.get('rateLimit', context.getHandler()) ?? 1; + const request = context.switchToHttp().getRequest() as UserRequest; + const response = context.switchToHttp().getResponse(); + let limiter = this.rateLimiterPublic; + if (request.user?.type === 'api-key') limiter = this.rateLimiterApiKey; + else if (request.user?.type === 'user') + limiter = this.rateLimiterAuthenticated; + try { + const ip = getClientIp(request); + const result = await limiter.consume(ip.replace(/^.*:/, ''), points); + response.header('Retry-After', Math.ceil(result.msBeforeNext / 1000)); + response.header('X-RateLimit-Limit', points); + response.header('X-Retry-Remaining', result.remainingPoints); + response.header( + 'X-Retry-Reset', + new Date(Date.now() + result.msBeforeNext).toUTCString(), + ); + } catch (result) { + response.header('Retry-After', Math.ceil(result.msBeforeNext / 1000)); + throw new HttpException( + RATE_LIMIT_EXCEEDED, + HttpStatus.TOO_MANY_REQUESTS, + ); + } + return next.handle(); + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..ec358418 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,36 @@ +import { ValidationPipe } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { NestExpressApplication } from '@nestjs/platform-express'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { promises } from 'fs'; +import helmet from 'helmet'; +import { join } from 'path'; +import responseTime from 'response-time'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + app.useGlobalPipes(new ValidationPipe()); + app.use(helmet()); + app.enableCors(); + + const pkg = JSON.parse( + await promises.readFile(join('.', 'package.json'), 'utf8'), + ); + + const options = new DocumentBuilder() + .setTitle('API') + .setDescription('API description') + .setVersion(pkg.version) + .build(); + const document = SwaggerModule.createDocument(app, options); + SwaggerModule.setup('api', app, document); + + app.setGlobalPrefix('v1'); + app.use(responseTime()); + app.useStaticAssets(join(__dirname, '..', 'static')); + + await app.listen(process.env.PORT ?? 3000); +} +bootstrap(); diff --git a/src/middleware/api-logger.middleware.ts b/src/middleware/api-logger.middleware.ts new file mode 100644 index 00000000..d5134235 --- /dev/null +++ b/src/middleware/api-logger.middleware.ts @@ -0,0 +1,47 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { NextFunction, Response } from 'express'; +import { Configuration } from '../config/configuration.interface'; +import { UserRequest } from '../modules/auth/auth.interface'; +import { ElasticSearchService } from '../providers/elasticsearch/elasticsearch.service'; + +@Injectable() +export class ApiLoggerMiddleware implements NestMiddleware { + constructor( + private configService: ConfigService, + private elasticSearchService: ElasticSearchService, + ) {} + + use(request: UserRequest, res: Response, next: NextFunction) { + const config = this.configService.get( + 'tracking', + ); + let date = new Date(); + res.on('finish', () => { + let authorizationKey = ''; + if (typeof request.query.api_key === 'string') + authorizationKey = request.query.api_key.replace('Bearer ', ''); + else if (typeof request.headers['x-api-key'] === 'string') + authorizationKey = request.headers['x-api-key'].replace('Bearer ', ''); + else if (request.headers.authorization) + authorizationKey = request.headers.authorization.replace('Bearer ', ''); + const obj = { + date, + method: request.method, + protocol: request.protocol, + path: request.path, + authorization: authorizationKey, + duration: new Date().getTime() - date.getTime(), + status: res.statusCode, + }; + if ( + config.mode === 'all' || + config.mode === request.user?.type || + (config.mode === 'api-key-or-user' && + (request.user?.type === 'api-key' || request.user?.type === 'user')) + ) + this.elasticSearchService.index(config.index, obj); + }); + next(); + } +} diff --git a/src/middleware/json-body.middleware.ts b/src/middleware/json-body.middleware.ts new file mode 100644 index 00000000..a4239dec --- /dev/null +++ b/src/middleware/json-body.middleware.ts @@ -0,0 +1,10 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import * as bodyParser from 'body-parser'; +import { NextFunction, Request, Response } from 'express'; + +@Injectable() +export class JsonBodyMiddleware implements NestMiddleware { + use(req: Request, res: Response, next: NextFunction) { + bodyParser.json()(req, res, next); + } +} diff --git a/src/middleware/raw-body.middleware.ts b/src/middleware/raw-body.middleware.ts new file mode 100644 index 00000000..8b00e380 --- /dev/null +++ b/src/middleware/raw-body.middleware.ts @@ -0,0 +1,10 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import * as bodyParser from 'body-parser'; +import { NextFunction, Request, Response } from 'express'; + +@Injectable() +export class RawBodyMiddleware implements NestMiddleware { + use(req: Request, res: Response, next: NextFunction) { + bodyParser.raw({ type: '*/*' })(req, res, next); + } +} diff --git a/src/modules/api-keys/api-keys-group.controller.ts b/src/modules/api-keys/api-keys-group.controller.ts new file mode 100644 index 00000000..3322f1a6 --- /dev/null +++ b/src/modules/api-keys/api-keys-group.controller.ts @@ -0,0 +1,135 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Post, + Put, + Query, + Req, +} from '@nestjs/common'; +import { ApiKey } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { UserRequest } from '../auth/auth.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { + CreateApiKeyDto, + ReplaceApiKeyDto, + UpdateApiKeyDto, +} from './api-keys.dto'; +import { ApiKeysService } from './api-keys.service'; + +@Controller('groups/:groupId/api-keys') +export class ApiKeyGroupController { + constructor(private apiKeysService: ApiKeysService) {} + + /** Create an API key for a group */ + @Post() + @AuditLog('create-api-key') + @Scopes('group-{groupId}:write-api-key-*') + async create( + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: CreateApiKeyDto, + ): Promise> { + return this.apiKeysService.createApiKeyForGroup(groupId, data); + } + + /** Get API keys for a group */ + @Get() + @Scopes('group-{groupId}:read-api-key-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.apiKeysService.getApiKeysForGroup(groupId, { + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Get API key scopes for a group */ + @Get('scopes') + @Scopes('group-{groupId}:write-api-key-*') + async scopes( + @Param('groupId', ParseIntPipe) groupId: number, + ): Promise> { + return this.apiKeysService.getApiKeyScopesForGroup(groupId); + } + + /** Get an API key */ + @Get(':id') + @Scopes('group-{groupId}:read-api-key-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.getApiKeyForGroup(groupId, id); + } + + /** Update an API key */ + @Patch(':id') + @AuditLog('update-api-key') + @Scopes('group-{groupId}:write-api-key-{id}') + async update( + @Body() data: UpdateApiKeyDto, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.updateApiKeyForGroup(groupId, id, data); + } + + /** Replace an API key */ + @Put(':id') + @AuditLog('update-api-key') + @Scopes('group-{groupId}:write-api-key-{id}') + async replace( + @Body() data: ReplaceApiKeyDto, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.updateApiKeyForGroup(groupId, id, data); + } + + /** Delete an API key */ + @Delete(':id') + @AuditLog('delete-api-key') + @Scopes('group-{groupId}:delete-api-key-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.deleteApiKeyForGroup(groupId, id); + } + + /** Get logs for an API key */ + @Get(':id/logs') + @Scopes('group-{groupId}:read-api-key-logs-*') + async getLogs( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + ): Promise[]> { + return this.apiKeysService.getApiKeyLogsForGroup(groupId, id, { + take, + cursor, + where, + }); + } +} diff --git a/src/modules/api-keys/api-keys-user.controller.ts b/src/modules/api-keys/api-keys-user.controller.ts new file mode 100644 index 00000000..c1e89d38 --- /dev/null +++ b/src/modules/api-keys/api-keys-user.controller.ts @@ -0,0 +1,130 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Post, + Put, + Query, + Req, +} from '@nestjs/common'; +import { ApiKey } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { UserRequest } from '../auth/auth.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { + CreateApiKeyDto, + ReplaceApiKeyDto, + UpdateApiKeyDto, +} from './api-keys.dto'; +import { ApiKeysService } from './api-keys.service'; + +@Controller('users/:userId/api-keys') +export class ApiKeyUserController { + constructor(private apiKeysService: ApiKeysService) {} + + /** Create an API key for a user */ + @Post() + @Scopes('user-{userId}:write-api-key-*') + async create( + @Param('userId', ParseIntPipe) userId: number, + @Body() data: CreateApiKeyDto, + ): Promise> { + return this.apiKeysService.createApiKeyForUser(userId, data); + } + + /** Get API keys for a user */ + @Get() + @Scopes('user-{userId}:read-api-key-*') + async getAll( + @Param('userId', ParseIntPipe) userId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.apiKeysService.getApiKeysForUser(userId, { + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Get API key scopes for a user */ + @Get('scopes') + @Scopes('user-{userId}:write-api-key-*') + async scopes( + @Param('userId', ParseIntPipe) userId: number, + ): Promise> { + return this.apiKeysService.getApiKeyScopesForUser(userId); + } + + /** Get an API key */ + @Get(':id') + @Scopes('user-{userId}:read-api-key-{id}') + async get( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.getApiKeyForUser(userId, id); + } + + /** Update an API key */ + @Patch(':id') + @Scopes('user-{userId}:write-api-key-{id}') + async update( + @Body() data: UpdateApiKeyDto, + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.updateApiKeyForUser(userId, id, data); + } + + /** Replace an API key */ + @Put(':id') + @Scopes('user-{userId}:write-api-key-{id}') + async replace( + @Body() data: ReplaceApiKeyDto, + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.updateApiKeyForUser(userId, id, data); + } + + /** Delete an API key */ + @Delete(':id') + @Scopes('user-{userId}:delete-api-key-{id}') + async remove( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.apiKeysService.deleteApiKeyForUser(userId, id); + } + + /** Get logs for an API key */ + @Get(':id/logs') + @Scopes('user-{userId}:read-api-key-logs-*') + async getLogs( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + ): Promise[]> { + return this.apiKeysService.getApiKeyLogsForUser(userId, id, { + take, + cursor, + where, + }); + } +} diff --git a/src/modules/api-keys/api-keys.dto.ts b/src/modules/api-keys/api-keys.dto.ts new file mode 100644 index 00000000..efc98f61 --- /dev/null +++ b/src/modules/api-keys/api-keys.dto.ts @@ -0,0 +1,76 @@ +import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +export class CreateApiKeyDto { + @IsString() + @IsOptional() + description?: string; + + @IsString() + @IsOptional() + name?: string; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + scopes?: string[]; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + ipRestrictions?: string[]; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + referrerRestrictions?: string[]; +} + +export class UpdateApiKeyDto { + @IsString() + @IsOptional() + description?: string; + + @IsString() + @IsOptional() + name?: string; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + scopes?: string[]; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + ipRestrictions?: string[]; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + referrerRestrictions?: string[]; +} + +export class ReplaceApiKeyDto { + @IsString() + @IsNotEmpty() + description!: string; + + @IsString() + @IsNotEmpty() + name!: string; + + @IsArray() + @IsString({ each: true }) + @IsNotEmpty() + scopes!: string[]; + + @IsArray() + @IsString({ each: true }) + @IsNotEmpty() + ipRestrictions!: string[]; + + @IsArray() + @IsString({ each: true }) + @IsNotEmpty() + referrerRestrictions!: string[]; +} diff --git a/src/modules/api-keys/api-keys.module.ts b/src/modules/api-keys/api-keys.module.ts new file mode 100644 index 00000000..8d22250a --- /dev/null +++ b/src/modules/api-keys/api-keys.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ElasticSearchModule } from '../../providers/elasticsearch/elasticsearch.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { ApiKeyGroupController } from './api-keys-group.controller'; +import { ApiKeyUserController } from './api-keys-user.controller'; +import { ApiKeysService } from './api-keys.service'; + +@Module({ + imports: [PrismaModule, TokensModule, ConfigModule, ElasticSearchModule], + controllers: [ApiKeyGroupController, ApiKeyUserController], + providers: [ApiKeysService], + exports: [ApiKeysService], +}) +export class ApiKeysModule {} diff --git a/src/modules/api-keys/api-keys.service.ts b/src/modules/api-keys/api-keys.service.ts new file mode 100644 index 00000000..db00fa37 --- /dev/null +++ b/src/modules/api-keys/api-keys.service.ts @@ -0,0 +1,449 @@ +import { + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { InputJsonValue, JsonValue, Prisma } from '@prisma/client'; +import { ApiKey } from '@prisma/client'; +import QuickLRU from 'quick-lru'; +import { + API_KEY_NOT_FOUND, + UNAUTHORIZED_RESOURCE, + USER_NOT_FOUND, +} from '../../errors/errors.constants'; +import { groupOwnerScopes, userScopes } from '../../helpers/scopes'; +import { ElasticSearchService } from '../../providers/elasticsearch/elasticsearch.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { TokensService } from '../../providers/tokens/tokens.service'; + +@Injectable() +export class ApiKeysService { + private lru = new QuickLRU({ + maxSize: this.configService.get('caching.apiKeyLruSize') ?? 100, + }); + + constructor( + private prisma: PrismaService, + private tokensService: TokensService, + private configService: ConfigService, + private elasticSearchService: ElasticSearchService, + ) {} + + async createApiKeyForGroup( + groupId: number, + data: Omit, 'group'>, + ): Promise { + const apiKey = await this.tokensService.generateRandomString(); + data.scopes = await this.cleanScopesForGroup(groupId, data.scopes); + return this.prisma.apiKey.create({ + data: { ...data, apiKey, group: { connect: { id: groupId } } }, + }); + } + async createApiKeyForUser( + userId: number, + data: Omit, 'user'>, + ): Promise { + const apiKey = await this.tokensService.generateRandomString(); + data.scopes = await this.cleanScopesForUser(userId, data.scopes); + return this.prisma.apiKey.create({ + data: { ...data, apiKey, user: { connect: { id: userId } } }, + }); + } + + async getApiKeysForGroup( + groupId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.ApiKeyWhereUniqueInput; + where?: Prisma.ApiKeyWhereInput; + orderBy?: Prisma.ApiKeyOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const apiKey = await this.prisma.apiKey.findMany({ + skip, + take, + cursor, + where: { ...where, group: { id: groupId } }, + orderBy, + }); + return apiKey.map((group) => this.prisma.expose(group)); + } catch (error) { + return []; + } + } + async getApiKeysForUser( + userId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.ApiKeyWhereUniqueInput; + where?: Prisma.ApiKeyWhereInput; + orderBy?: Prisma.ApiKeyOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const apiKey = await this.prisma.apiKey.findMany({ + skip, + take, + cursor, + where: { ...where, user: { id: userId } }, + orderBy, + }); + return apiKey.map((user) => this.prisma.expose(user)); + } catch (error) { + return []; + } + } + + async getApiKeyForGroup( + groupId: number, + id: number, + ): Promise> { + const apiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!apiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (apiKey.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(apiKey); + } + async getApiKeyForUser(userId: number, id: number): Promise> { + const apiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!apiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (apiKey.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(apiKey); + } + + async getApiKeyFromKey(key: string): Promise> { + if (this.lru.has(key)) return this.lru.get(key); + const apiKey = await this.prisma.apiKey.findFirst({ + where: { apiKey: key }, + }); + if (!apiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + this.lru.set(key, apiKey); + return this.prisma.expose(apiKey); + } + + async updateApiKeyForGroup( + groupId: number, + id: number, + data: Prisma.ApiKeyUpdateInput, + ): Promise> { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + data.scopes = await this.cleanScopesForGroup(groupId, data.scopes); + const apiKey = await this.prisma.apiKey.update({ + where: { id }, + data, + }); + this.lru.delete(testApiKey.apiKey); + return this.prisma.expose(apiKey); + } + async updateApiKeyForUser( + userId: number, + id: number, + data: Prisma.ApiKeyUpdateInput, + ): Promise> { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + data.scopes = await this.cleanScopesForUser(userId, data.scopes); + const apiKey = await this.prisma.apiKey.update({ + where: { id }, + data, + }); + this.lru.delete(testApiKey.apiKey); + return this.prisma.expose(apiKey); + } + + async replaceApiKeyForGroup( + groupId: number, + id: number, + data: Prisma.ApiKeyCreateInput, + ): Promise> { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + data.scopes = await this.cleanScopesForGroup(groupId, data.scopes); + const apiKey = await this.prisma.apiKey.update({ + where: { id }, + data, + }); + this.lru.delete(testApiKey.apiKey); + return this.prisma.expose(apiKey); + } + async replaceApiKeyForUser( + userId: number, + id: number, + data: Prisma.ApiKeyCreateInput, + ): Promise> { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + data.scopes = await this.cleanScopesForUser(userId, data.scopes); + const apiKey = await this.prisma.apiKey.update({ + where: { id }, + data, + }); + this.lru.delete(testApiKey.apiKey); + return this.prisma.expose(apiKey); + } + + async deleteApiKeyForGroup( + groupId: number, + id: number, + ): Promise> { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const apiKey = await this.prisma.apiKey.delete({ + where: { id }, + }); + this.lru.delete(testApiKey.apiKey); + return this.prisma.expose(apiKey); + } + async deleteApiKeyForUser( + userId: number, + id: number, + ): Promise> { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const apiKey = await this.prisma.apiKey.delete({ + where: { id }, + }); + this.lru.delete(testApiKey.apiKey); + return this.prisma.expose(apiKey); + } + + async getApiKeyLogsForGroup( + groupId: number, + id: number, + params: { + take?: number; + cursor?: { id?: number }; + where?: { after?: string }; + }, + ) { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.getApiLogsFromKey(testApiKey.apiKey, params); + } + async getApiKeyLogsForUser( + userId: number, + id: number, + params: { + take?: number; + cursor?: { id?: number }; + where?: { after?: string }; + }, + ) { + const testApiKey = await this.prisma.apiKey.findUnique({ + where: { id }, + }); + if (!testApiKey) throw new NotFoundException(API_KEY_NOT_FOUND); + if (testApiKey.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.getApiLogsFromKey(testApiKey.apiKey, params); + } + + /** + * Remove any unauthorized scopes in an API key for a user + * This should run when a user's permissions have changed, for example + * if they are removed from a group; this will remove any API scopes + * they don't have access to anymore from that API key + */ + async removeUnauthorizedScopesForUser(userId: number): Promise { + const userApiKeys = await this.prisma.apiKey.findMany({ + where: { user: { id: userId } }, + }); + if (!userApiKeys.length) return; + const scopesAllowed = await this.getApiKeyScopesForUser(userId); + for await (const apiKey of userApiKeys) { + const currentScopes = (apiKey.scopes ?? []) as string[]; + const newScopes = currentScopes.filter((i) => + Object.keys(scopesAllowed).includes(i), + ); + if (currentScopes.length !== newScopes.length) + this.prisma.apiKey.update({ + where: { id: apiKey.id }, + data: { scopes: newScopes }, + }); + } + } + + private async getApiLogsFromKey( + apiKey: string, + params: { + take?: number; + cursor?: { id?: number }; + where?: { after?: string }; + }, + ): Promise[]> { + const now = new Date(); + now.setDate( + now.getDate() - + this.configService.get('tracking.deleteOldLogsDays'), + ); + const result = await this.elasticSearchService.search({ + index: this.configService.get('tracking.index'), + from: params.cursor?.id, + body: { + query: { + bool: { + must: [ + { + match: { + authorization: apiKey, + }, + }, + { + range: { + date: { + gte: params.where?.after + ? new Date( + new Date().getTime() - + new Date(params.where?.after).getTime(), + ) + : now, + }, + }, + }, + ], + }, + }, + sort: [ + { + date: { order: 'desc' }, + }, + ], + size: params.take ?? 100, + }, + }); + try { + return result.body.hits.hits.map( + (item: { + _index: string; + _type: '_doc'; + _id: string; + _score: any; + _source: Record; + }) => ({ ...item._source, id: item._id }), + ); + } catch (error) {} + return []; + } + + private async cleanScopesForGroup( + groupId: number, + scopes: InputJsonValue, + ): Promise { + if (!Array.isArray(scopes)) return []; + return (scopes as string[]).filter((i) => + Object.keys( + Object.keys(groupOwnerScopes).map((i) => + i.replace('{groupId}', groupId.toString()), + ), + ).includes(i), + ); + } + private async cleanScopesForUser( + userId: number, + scopes: InputJsonValue, + allowedScopes?: Record, + ): Promise { + if (!Array.isArray(scopes)) return []; + if (!allowedScopes) + allowedScopes = await this.getApiKeyScopesForUser(userId); + return (scopes as string[]).filter((i) => + Object.keys(allowedScopes).includes(i), + ); + } + + /** + * Clean all API keys for a user, i.e., make sure they don't have + * any scopes they're not allowed to have + */ + async cleanAllApiKeysForUser(userId: number): Promise { + const apiKeys = await this.prisma.apiKey.findMany({ + where: { user: { id: userId } }, + select: { id: true, scopes: true }, + }); + if (!apiKeys.length) return; + const allowedScopes = await this.getApiKeyScopesForUser(userId); + for await (const apiKey of apiKeys) + await this.prisma.apiKey.update({ + where: { id: apiKey.id }, + data: { + scopes: await this.cleanScopesForUser( + userId, + apiKey.scopes, + allowedScopes, + ), + }, + }); + } + + getApiKeyScopesForGroup(groupId: number): Record { + const scopes: Record = {}; + Object.keys(groupOwnerScopes).forEach( + (key) => + (scopes[key.replace('{groupId}', groupId.toString())] = + groupOwnerScopes[key]), + ); + return scopes; + } + + async getApiKeyScopesForUser( + userId: number, + ): Promise> { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { role: true }, + }); + if (!userId) throw new NotFoundException(USER_NOT_FOUND); + const scopes: Record = {}; + if (user.role === 'SUDO') { + scopes['*'] = 'Do everything (USE WITH CAUTION)'; + scopes['user-*:*'] = 'CRUD users'; + scopes['group-*:*'] = 'CRUD groups'; + } + Object.keys(userScopes).forEach( + (key) => + (scopes[key.replace('{userId}', userId.toString())] = userScopes[key]), + ); + return scopes; + } +} diff --git a/src/modules/approved-subnets/approved-subnets.controller.ts b/src/modules/approved-subnets/approved-subnets.controller.ts new file mode 100644 index 00000000..3c314847 --- /dev/null +++ b/src/modules/approved-subnets/approved-subnets.controller.ts @@ -0,0 +1,61 @@ +import { + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Query, +} from '@nestjs/common'; +import { ApprovedSubnet } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { ApprovedSubnetsService } from './approved-subnets.service'; + +@Controller('users/:userId/approved-subnets') +export class ApprovedSubnetController { + constructor(private approvedSubnetsService: ApprovedSubnetsService) {} + + /** Get approved subnets for a user */ + @Get() + @Scopes('user-{userId}:read-approved-subnet-*') + async getAll( + @Param('userId', ParseIntPipe) userId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.approvedSubnetsService.getApprovedSubnets(userId, { + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Get an approved subnet for a user */ + @Get(':id') + @Scopes('user-{userId}:read-approved-subnet-{id}') + async get( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.approvedSubnetsService.getApprovedSubnet(userId, id); + } + + /** Delete an approved subnet for a user */ + @Delete(':id') + @Scopes('user-{userId}:delete-approved-subnet-{id}') + async remove( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.approvedSubnetsService.deleteApprovedSubnet(userId, id); + } +} diff --git a/src/modules/approved-subnets/approved-subnets.module.ts b/src/modules/approved-subnets/approved-subnets.module.ts new file mode 100644 index 00000000..d6aec237 --- /dev/null +++ b/src/modules/approved-subnets/approved-subnets.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { GeolocationModule } from '../../providers/geolocation/geolocation.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { ApprovedSubnetController } from './approved-subnets.controller'; +import { ApprovedSubnetsService } from './approved-subnets.service'; + +@Module({ + imports: [PrismaModule, ConfigModule, GeolocationModule], + controllers: [ApprovedSubnetController], + providers: [ApprovedSubnetsService], +}) +export class ApprovedSubnetsModule {} diff --git a/src/modules/approved-subnets/approved-subnets.service.ts b/src/modules/approved-subnets/approved-subnets.service.ts new file mode 100644 index 00000000..d1ac06cb --- /dev/null +++ b/src/modules/approved-subnets/approved-subnets.service.ts @@ -0,0 +1,122 @@ +import { + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { Prisma } from '@prisma/client'; +import { ApprovedSubnet } from '@prisma/client'; +import { compare, hash } from 'bcrypt'; +import anonymize from 'ip-anonymize'; +import { + APPROVED_SUBNET_NOT_FOUND, + UNAUTHORIZED_RESOURCE, +} from '../../errors/errors.constants'; +import { GeolocationService } from '../../providers/geolocation/geolocation.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; + +@Injectable() +export class ApprovedSubnetsService { + constructor( + private prisma: PrismaService, + private configService: ConfigService, + private geolocationService: GeolocationService, + ) {} + + async getApprovedSubnets( + userId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.ApprovedSubnetWhereUniqueInput; + where?: Prisma.ApprovedSubnetWhereInput; + orderBy?: Prisma.ApprovedSubnetOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const ApprovedSubnet = await this.prisma.approvedSubnet.findMany({ + skip, + take, + cursor, + where: { ...where, user: { id: userId } }, + orderBy, + }); + return ApprovedSubnet.map((user) => + this.prisma.expose(user), + ); + } catch (error) { + return []; + } + } + + async getApprovedSubnet( + userId: number, + id: number, + ): Promise> { + const ApprovedSubnet = await this.prisma.approvedSubnet.findUnique({ + where: { id }, + }); + if (!ApprovedSubnet) throw new NotFoundException(APPROVED_SUBNET_NOT_FOUND); + if (ApprovedSubnet.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + if (!ApprovedSubnet) throw new NotFoundException(APPROVED_SUBNET_NOT_FOUND); + return this.prisma.expose(ApprovedSubnet); + } + + async deleteApprovedSubnet( + userId: number, + id: number, + ): Promise> { + const testApprovedSubnet = await this.prisma.approvedSubnet.findUnique({ + where: { id }, + }); + if (!testApprovedSubnet) + throw new NotFoundException(APPROVED_SUBNET_NOT_FOUND); + if (testApprovedSubnet.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const ApprovedSubnet = await this.prisma.approvedSubnet.delete({ + where: { id }, + }); + return this.prisma.expose(ApprovedSubnet); + } + + async approveNewSubnet(userId: number, ipAddress: string) { + const subnet = await hash( + anonymize(ipAddress), + this.configService.get('security.saltRounds') ?? 10, + ); + const location = await this.geolocationService.getLocation(ipAddress); + const approved = await this.prisma.approvedSubnet.create({ + data: { + user: { connect: { id: userId } }, + subnet, + city: location?.city?.names?.en, + region: location?.subdivisions?.pop()?.names?.en, + timezone: location?.location?.time_zone, + countryCode: location?.country?.iso_code, + }, + }); + return this.prisma.expose(approved); + } + + /** + * Upsert a new subnet + * If this subnet already exists, skip; otherwise add it + */ + async upsertNewSubnet( + userId: number, + ipAddress: string, + ): Promise> { + const subnet = anonymize(ipAddress); + const previousSubnets = await this.prisma.approvedSubnet.findMany({ + where: { user: { id: userId } }, + }); + for await (const item of previousSubnets) { + if (await compare(subnet, item.subnet)) + return this.prisma.expose(item); + } + return this.approveNewSubnet(userId, ipAddress); + } +} diff --git a/src/modules/audit-logs/audit-log.constants.ts b/src/modules/audit-logs/audit-log.constants.ts new file mode 100644 index 00000000..09540b31 --- /dev/null +++ b/src/modules/audit-logs/audit-log.constants.ts @@ -0,0 +1 @@ +export const STAART_AUDIT_LOG_DATA = 'STAART_AUDIT_LOG_DATA'; diff --git a/src/modules/audit-logs/audit-log.decorator.ts b/src/modules/audit-logs/audit-log.decorator.ts new file mode 100644 index 00000000..b6ecbe58 --- /dev/null +++ b/src/modules/audit-logs/audit-log.decorator.ts @@ -0,0 +1,5 @@ +import { SetMetadata } from '@nestjs/common'; +import { STAART_AUDIT_LOG_DATA } from './audit-log.constants'; + +export const AuditLog = (...value: string[]) => + SetMetadata(STAART_AUDIT_LOG_DATA, value); diff --git a/src/modules/audit-logs/audit-logs-group.controller.ts b/src/modules/audit-logs/audit-logs-group.controller.ts new file mode 100644 index 00000000..dc87c9f2 --- /dev/null +++ b/src/modules/audit-logs/audit-logs-group.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'; +import { AuditLog } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { AuditLogsService } from './audit-logs.service'; + +@Controller('groups/:groupId/audit-logs') +export class AuditLogGroupController { + constructor(private auditLogsService: AuditLogsService) {} + + /** Get audit logs for a group */ + @Get() + @Scopes('group-{groupId}:read-audit-log-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.auditLogsService.getAuditLogs({ + skip, + take, + orderBy, + cursor, + where: { ...where, group: { id: groupId } }, + }); + } +} diff --git a/src/modules/audit-logs/audit-logs-user.controller.ts b/src/modules/audit-logs/audit-logs-user.controller.ts new file mode 100644 index 00000000..b948b7ee --- /dev/null +++ b/src/modules/audit-logs/audit-logs-user.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'; +import { AuditLog } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { AuditLogsService } from './audit-logs.service'; + +@Controller('users/:userId/audit-logs') +export class AuditLogUserController { + constructor(private auditLogsService: AuditLogsService) {} + + /** Get audit logs for a user */ + @Get() + @Scopes('user-{userId}:read-audit-log-*') + async getAll( + @Param('userId', ParseIntPipe) userId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.auditLogsService.getAuditLogs({ + skip, + take, + orderBy, + cursor, + where: { ...where, user: { id: userId } }, + }); + } +} diff --git a/src/modules/audit-logs/audit-logs.controller.ts b/src/modules/audit-logs/audit-logs.controller.ts new file mode 100644 index 00000000..2b0ecc1f --- /dev/null +++ b/src/modules/audit-logs/audit-logs.controller.ts @@ -0,0 +1,33 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { AuditLog } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { AuditLogsService } from './audit-logs.service'; + +@Controller('audit-logs') +export class AuditLogController { + constructor(private auditLogsService: AuditLogsService) {} + + /** Get audit logs for a group */ + @Get() + @Scopes('audit-log-*:read-info') + async getAll( + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.auditLogsService.getAuditLogs({ + skip, + take, + orderBy, + cursor, + where, + }); + } +} diff --git a/src/modules/audit-logs/audit-logs.module.ts b/src/modules/audit-logs/audit-logs.module.ts new file mode 100644 index 00000000..969bdd25 --- /dev/null +++ b/src/modules/audit-logs/audit-logs.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { AuditLogController } from './audit-logs.controller'; +import { AuditLogGroupController } from './audit-logs-group.controller'; +import { AuditLogUserController } from './audit-logs-user.controller'; +import { AuditLogsService } from './audit-logs.service'; + +@Module({ + imports: [PrismaModule], + controllers: [ + AuditLogController, + AuditLogGroupController, + AuditLogUserController, + ], + providers: [AuditLogsService], +}) +export class AuditLogsModule {} diff --git a/src/modules/audit-logs/audit-logs.service.ts b/src/modules/audit-logs/audit-logs.service.ts new file mode 100644 index 00000000..8f2b5340 --- /dev/null +++ b/src/modules/audit-logs/audit-logs.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import type { Prisma } from '@prisma/client'; +import { AuditLog } from '@prisma/client'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; + +@Injectable() +export class AuditLogsService { + constructor(private prisma: PrismaService) {} + + async getAuditLogs(params: { + skip?: number; + take?: number; + cursor?: Prisma.AuditLogWhereUniqueInput; + where?: Prisma.AuditLogWhereInput; + orderBy?: Prisma.AuditLogOrderByInput; + }): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const AuditLog = await this.prisma.auditLog.findMany({ + skip, + take, + cursor, + where, + orderBy, + include: { group: true, user: true }, + }); + return AuditLog.map((group) => this.prisma.expose(group)); + } catch (error) { + return []; + } + } +} diff --git a/src/modules/auth/auth.constants.ts b/src/modules/auth/auth.constants.ts new file mode 100644 index 00000000..37281161 --- /dev/null +++ b/src/modules/auth/auth.constants.ts @@ -0,0 +1 @@ +export const STAART_PUBLIC_ENDPOINT = 'STAART_PUBLIC_ENDPOINT'; diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts new file mode 100644 index 00000000..dbed2652 --- /dev/null +++ b/src/modules/auth/auth.controller.ts @@ -0,0 +1,176 @@ +import { + Body, + Controller, + Headers, + HttpCode, + HttpStatus, + Ip, + Post, +} from '@nestjs/common'; +import { User } from '@prisma/client'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { + ForgotPasswordDto, + LoginDto, + RegisterDto, + ResendEmailVerificationDto, + ResetPasswordDto, + TotpLoginDto, + VerifyEmailDto, +} from './auth.dto'; +import { TokenResponse, TotpTokenResponse } from './auth.interface'; +import { AuthService } from './auth.service'; +import { Public } from './public.decorator'; +import { RateLimit } from './rate-limit.decorator'; + +@Controller('auth') +@Public() +export class AuthController { + constructor(private authService: AuthService) {} + + /** Login to an account */ + @Post('login') + @RateLimit(10) + async login( + @Body() data: LoginDto, + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + @Body('origin') origin?: string, + ): Promise { + return this.authService.login( + ip, + userAgent, + data.email, + data.password, + data.code, + origin, + ); + } + + /** Create a new account */ + @Post('register') + @HttpCode(HttpStatus.CREATED) + @RateLimit(10) + async register( + @Ip() ip: string, + @Body() data: RegisterDto, + ): Promise> { + return this.authService.register(ip, data); + } + + /** Get a new access token using a refresh token */ + @Post('refresh') + @RateLimit(5) + async refresh( + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + @Body('token') refreshToken: string, + ): Promise { + return this.authService.refresh(ip, userAgent, refreshToken); + } + + /** Logout from a session */ + @Post('logout') + @RateLimit(5) + async logout( + @Body('token') refreshToken: string, + ): Promise<{ success: true }> { + await this.authService.logout(refreshToken); + return { success: true }; + } + + /** Approve a new subnet */ + @Post('approve-subnet') + @RateLimit(5) + async approveSubnet( + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + @Body('token') token: string, + ): Promise { + return this.authService.approveSubnet(ip, userAgent, token); + } + + /** Resend email verification link */ + @Post('resend-email-verification') + @RateLimit(10) + async resendVerify( + @Body() data: ResendEmailVerificationDto, + @Body('origin') origin?: string, + ) { + return this.authService.sendEmailVerification(data.email, true, origin); + } + + /** Verify a new email */ + @Post('verify-email') + async verifyEmail( + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + @Body() data: VerifyEmailDto, + @Body('origin') origin?: string, + ): Promise { + return this.authService.verifyEmail(ip, userAgent, data.token, origin); + } + + /** Send a password reset link */ + @Post('forgot-password') + @RateLimit(10) + async forgotPassword( + @Body() data: ForgotPasswordDto, + @Body('origin') origin?: string, + ) { + return this.authService.requestPasswordReset(data.email, origin); + } + + /** Reset password */ + @Post('reset-password') + @RateLimit(10) + async resetPassword( + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + @Body() data: ResetPasswordDto, + ): Promise { + return this.authService.resetPassword( + ip, + userAgent, + data.token, + data.password, + data.ignorePwnedPassword, + ); + } + + /** Login using TOTP */ + @Post('login/totp') + @RateLimit(10) + async totpLogin( + @Body() data: TotpLoginDto, + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + @Body('origin') origin?: string, + ): Promise { + return this.authService.loginWithTotp( + ip, + userAgent, + data.token, + data.code, + origin, + ); + } + + /** Login using a token */ + @Post('login/token') + @RateLimit(10) + async emailTokenLoginPost( + @Body('token') token: string, + @Ip() ip: string, + @Headers('User-Agent') userAgent: string, + ): Promise { + return this.authService.loginWithEmailToken(ip, userAgent, token); + } + + /** Merge two user accounts */ + @Post('merge-accounts') + @RateLimit(10) + async merge(@Body('token') token: string): Promise<{ success: true }> { + return this.authService.mergeUsers(token); + } +} diff --git a/src/modules/auth/auth.dto.ts b/src/modules/auth/auth.dto.ts new file mode 100644 index 00000000..4d62e79d --- /dev/null +++ b/src/modules/auth/auth.dto.ts @@ -0,0 +1,155 @@ +import { + IsBoolean, + IsEmail, + IsIn, + IsLocale, + IsNotEmpty, + IsOptional, + IsString, + IsUrl, + Length, + MinLength, +} from 'class-validator'; + +export class RegisterDto { + @IsString() + @IsNotEmpty() + @MinLength(3) + name!: string; + + @IsString() + @IsOptional() + origin?: string; + + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsBoolean() + @IsOptional() + checkLocationOnLogin?: boolean; + + @IsString() + @Length(2, 2) + @IsOptional() + countryCode?: string; + + @IsString() + @IsIn(['MALE', 'FEMALE', 'NONBINARY', 'UNKNOWN']) + @IsOptional() + gender?: 'MALE' | 'FEMALE' | 'NONBINARY' | 'UNKNOWN'; + + @IsIn(['ACCOUNT', 'UPDATES', 'PROMOTIONS']) + @IsOptional() + notificationEmails?: 'ACCOUNT' | 'UPDATES' | 'PROMOTIONS'; + + @IsString() + @IsOptional() + password?: string | null; + + @IsLocale() + @IsOptional() + prefersLanguage?: string; + + @IsString() + @IsIn(['NO_PREFERENCE', 'LIGHT', 'DARK']) + @IsOptional() + prefersColorScheme?: 'NO_PREFERENCE' | 'LIGHT' | 'DARK'; + + @IsString() + @IsIn(['NO_PREFERENCE', 'REDUCE']) + @IsOptional() + prefersReducedMotion?: 'NO_PREFERENCE' | 'REDUCE'; + + @IsUrl() + @IsOptional() + profilePictureUrl?: string; + + @IsString() + @IsOptional() + timezone?: string; + + @IsBoolean() + @IsOptional() + ignorePwnedPassword?: boolean; +} + +export class ResendEmailVerificationDto { + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsString() + @IsOptional() + origin?: string; +} + +export class ForgotPasswordDto { + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsString() + @IsOptional() + origin?: string; +} + +export class ResetPasswordDto { + @IsString() + @IsNotEmpty() + token!: string; + + @IsString() + @MinLength(8) + @IsNotEmpty() + password!: string; + + @IsBoolean() + @IsOptional() + ignorePwnedPassword?: boolean; +} + +export class LoginDto { + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsString() + @MinLength(8) + @IsOptional() + password?: string; + + @IsString() + @IsOptional() + origin?: string; + + @IsString() + @Length(6) + @IsOptional() + code?: string; +} + +export class TotpLoginDto { + @IsString() + @IsNotEmpty() + token!: string; + + @IsString() + @IsOptional() + origin?: string; + + @IsString() + @Length(6) + @IsNotEmpty() + code!: string; +} + +export class VerifyEmailDto { + @IsString() + @IsNotEmpty() + token!: string; + + @IsString() + @IsOptional() + origin?: string; +} diff --git a/src/modules/auth/auth.interface.ts b/src/modules/auth/auth.interface.ts new file mode 100644 index 00000000..58554aeb --- /dev/null +++ b/src/modules/auth/auth.interface.ts @@ -0,0 +1,41 @@ +import type { Request as NestRequest } from '@nestjs/common'; +import { UserRole } from '@prisma/client'; +import type { Request as ExpressRequest } from 'express'; + +export type MfaMethod = 'NONE' | 'SMS' | 'TOTP' | 'EMAIL'; + +export interface AccessTokenClaims { + id: number; + scopes: string[]; + sessionId: number; + role?: UserRole; +} + +export interface TokenResponse { + accessToken: string; + refreshToken: string; +} + +export interface TotpTokenResponse { + totpToken: string; + type: MfaMethod; + multiFactorRequired: true; +} + +export interface AccessTokenParsed { + id: number; + scopes: string[]; + type: 'user' | 'api-key'; + sessionId?: number; + role?: UserRole; +} + +export interface MfaTokenPayload { + id: number; + type: MfaMethod; +} + +type CombinedRequest = ExpressRequest & typeof NestRequest; +export interface UserRequest extends CombinedRequest { + user: AccessTokenParsed; +} diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts new file mode 100644 index 00000000..6a144c52 --- /dev/null +++ b/src/modules/auth/auth.module.ts @@ -0,0 +1,34 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { PassportModule } from '@nestjs/passport'; +import { GeolocationModule } from '../../providers/geolocation/geolocation.module'; +import { MailModule } from '../../providers/mail/mail.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { PwnedModule } from '../../providers/pwned/pwned.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { TwilioModule } from '../../providers/twilio/twilio.module'; +import { ApiKeysModule } from '../api-keys/api-keys.module'; +import { ApprovedSubnetsModule } from '../approved-subnets/approved-subnets.module'; +import { ApprovedSubnetsService } from '../approved-subnets/approved-subnets.service'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { StaartStrategy } from './staart.strategy'; + +@Module({ + imports: [ + PassportModule.register({ defaultStrategy: 'jwt' }), + PrismaModule, + MailModule, + TokensModule, + ConfigModule, + PwnedModule, + ApiKeysModule, + TwilioModule, + GeolocationModule, + ApprovedSubnetsModule, + ], + controllers: [AuthController], + exports: [AuthService], + providers: [AuthService, StaartStrategy, ApprovedSubnetsService], +}) +export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts new file mode 100644 index 00000000..f731f030 --- /dev/null +++ b/src/modules/auth/auth.service.ts @@ -0,0 +1,884 @@ +import { + BadRequestException, + ConflictException, + Injectable, + NotFoundException, + UnauthorizedException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { Prisma } from '@prisma/client'; +import { Email, MfaMethod, User } from '@prisma/client'; +import { compare, hash } from 'bcrypt'; +import { createHash } from 'crypto'; +import got from 'got/dist/source'; +import anonymize from 'ip-anonymize'; +import { authenticator } from 'otplib'; +import { createRandomBytes, createDigest } from '@otplib/plugin-crypto'; +import { keyEncoder, keyDecoder } from '@otplib/plugin-thirty-two'; +import qrcode from 'qrcode'; +import randomColor from 'randomcolor'; +import UAParser from 'ua-parser-js'; +import { + COMPROMISED_PASSWORD, + EMAIL_USER_CONFLICT, + EMAIL_VERIFIED_CONFLICT, + INVALID_CREDENTIALS, + INVALID_MFA_CODE, + MFA_BACKUP_CODE_USED, + MFA_ENABLED_CONFLICT, + MFA_NOT_ENABLED, + MFA_PHONE_NOT_FOUND, + NO_EMAILS, + NO_TOKEN_PROVIDED, + SESSION_NOT_FOUND, + UNVERIFIED_EMAIL, + UNVERIFIED_LOCATION, + USER_NOT_FOUND, +} from '../../errors/errors.constants'; +import { safeEmail } from '../../helpers/safe-email'; +import { GeolocationService } from '../../providers/geolocation/geolocation.service'; +import { MailService } from '../../providers/mail/mail.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { PwnedService } from '../../providers/pwned/pwned.service'; +import { + APPROVE_SUBNET_TOKEN, + EMAIL_MFA_TOKEN, + EMAIL_VERIFY_TOKEN, + LOGIN_ACCESS_TOKEN, + MERGE_ACCOUNTS_TOKEN, + MULTI_FACTOR_TOKEN, + PASSWORD_RESET_TOKEN, +} from '../../providers/tokens/tokens.constants'; +import { TokensService } from '../../providers/tokens/tokens.service'; +import { TwilioService } from '../../providers/twilio/twilio.service'; +import { ApprovedSubnetsService } from '../approved-subnets/approved-subnets.service'; +import { RegisterDto } from './auth.dto'; +import { + AccessTokenClaims, + MfaTokenPayload, + TokenResponse, + TotpTokenResponse, +} from './auth.interface'; +import { + groupAdminScopes, + groupMemberScopes, + groupOwnerScopes, + userScopes, +} from '../../helpers/scopes'; +import axios from 'axios'; + +@Injectable() +export class AuthService { + authenticator: typeof authenticator; + + constructor( + private prisma: PrismaService, + private email: MailService, + private configService: ConfigService, + private pwnedService: PwnedService, + private tokensService: TokensService, + private geolocationService: GeolocationService, + private approvedSubnetsService: ApprovedSubnetsService, + private twilioService: TwilioService, + ) { + this.authenticator = authenticator.create({ + window: [ + this.configService.get('security.totpWindowPast') ?? 0, + this.configService.get('security.totpWindowFuture') ?? 0, + ], + keyEncoder, + keyDecoder, + createDigest, + createRandomBytes, + }); + } + + async login( + ipAddress: string, + userAgent: string, + email: string, + password?: string, + code?: string, + origin?: string, + ): Promise { + const emailSafe = safeEmail(email); + const user = await this.prisma.user.findFirst({ + where: { emails: { some: { emailSafe } } }, + include: { + emails: true, + prefersEmail: true, + }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (!user.active) + await this.prisma.user.update({ + where: { id: user.id }, + data: { active: true }, + }); + if (!user.emails.find((i) => i.emailSafe === emailSafe)?.isVerified) + throw new UnauthorizedException(UNVERIFIED_EMAIL); + if (!password || !user.password) return this.mfaResponse(user, 'EMAIL'); + if (!user.prefersEmail) throw new BadRequestException(NO_EMAILS); + if (!(await compare(password, user.password))) + throw new UnauthorizedException(INVALID_CREDENTIALS); + if (code) + return this.loginUserWithTotpCode( + ipAddress, + userAgent, + user.id, + code, + origin, + ); + if (user.twoFactorMethod !== 'NONE') return this.mfaResponse(user); + await this.checkLoginSubnet( + ipAddress, + userAgent, + user.checkLocationOnLogin, + user.id, + origin, + ); + return this.loginResponse(ipAddress, userAgent, user); + } + + async register(ipAddress: string, _data: RegisterDto): Promise> { + const { email, origin, ...data } = _data; + const emailSafe = safeEmail(email); + const testUser = await this.prisma.user.findFirst({ + where: { emails: { some: { emailSafe } } }, + }); + if (testUser) throw new ConflictException(EMAIL_USER_CONFLICT); + const ignorePwnedPassword = !!data.ignorePwnedPassword; + delete data.ignorePwnedPassword; + + if (data.name) + data.name = data.name + .split(' ') + .map((word, index) => + index === 0 || index === data.name.split(' ').length + ? (word.charAt(0) ?? '').toUpperCase() + + (word.slice(1) ?? '').toLowerCase() + : word, + ) + .join(' '); + if (data.password) + data.password = await this.hashAndValidatePassword( + data.password, + ignorePwnedPassword, + ); + let initials = data.name.trim().substr(0, 2).toUpperCase(); + if (data.name.includes(' ')) + initials = data.name + .split(' ') + .map((i) => i.trim().substr(0, 1)) + .join('') + .toUpperCase(); + data.profilePictureUrl = + data.profilePictureUrl ?? + `https://ui-avatars.com/api/?name=${initials}&background=${randomColor({ + luminosity: 'light', + }).replace('#', '')}&color=000000`; + + if (!data.gender) { + try { + const prediction = await axios.get<{ + name: string; + gender: 'male' | 'female'; + probability: number; + count: number; + }>(`https://api.genderize.io/?name=${data.name.split(' ')[0]}`); + if ( + prediction.data.probability > 0.5 && + prediction.data.gender === 'male' + ) + data.gender = 'MALE'; + if ( + prediction.data.probability > 0.5 && + prediction.data.gender === 'female' + ) + data.gender = 'FEMALE'; + } catch (error) {} + } + + if (this.configService.get('gravatar.enabled')) { + for await (const emailString of [email, emailSafe]) { + const md5Email = createHash('md5').update(emailString).digest('hex'); + try { + const img = await got( + `https://www.gravatar.com/avatar/${md5Email}?d=404`, + { responseType: 'buffer' }, + ); + if (img.body.byteLength > 1) + data.profilePictureUrl = `https://www.gravatar.com/avatar/${md5Email}?d=mp`; + } catch (error) {} + } + } + + let id: number | undefined = undefined; + while (!id) { + id = Number( + `10${await this.tokensService.generateRandomString(6, 'numeric')}`, + ); + const users = await this.prisma.user.findMany({ where: { id }, take: 1 }); + if (users.length) id = undefined; + } + const user = await this.prisma.user.create({ + data: { + ...data, + id, + emails: { + create: { email: email, emailSafe }, + }, + }, + include: { emails: { select: { id: true } } }, + }); + if (user.emails[0]?.id) + await this.prisma.user.update({ + where: { id: user.id }, + data: { prefersEmail: { connect: { id: user.emails[0].id } } }, + }); + // In testing, we auto-approve the email + if (process.env.TEST) { + const emailId = user.emails[0]?.id; + if (emailId) + await this.prisma.email.update({ + where: { id: emailId }, + data: { isVerified: true }, + }); + } else await this.sendEmailVerification(email, false, origin); + await this.approvedSubnetsService.approveNewSubnet(user.id, ipAddress); + return this.prisma.expose(user); + } + + async sendEmailVerification(email: string, resend = false, origin?: string) { + const emailSafe = safeEmail(email); + const emailDetails = await this.prisma.email.findFirst({ + where: { emailSafe }, + include: { user: true }, + }); + if (!emailDetails) throw new NotFoundException(USER_NOT_FOUND); + if (emailDetails.isVerified) + throw new ConflictException(EMAIL_VERIFIED_CONFLICT); + this.email.send({ + to: `"${emailDetails.user.name}" <${email}>`, + template: resend + ? 'auth/resend-email-verification' + : 'auth/email-verification', + data: { + name: emailDetails.user.name, + days: 7, + link: `${ + origin ?? this.configService.get('frontendUrl') + }/auth/link/verify-email?token=${this.tokensService.signJwt( + EMAIL_VERIFY_TOKEN, + { id: emailDetails.id }, + '7d', + )}`, + }, + }); + return { queued: true }; + } + + async refresh( + ipAddress: string, + userAgent: string, + token: string, + ): Promise { + if (!token) throw new UnprocessableEntityException(NO_TOKEN_PROVIDED); + const session = await this.prisma.session.findFirst({ + where: { token }, + include: { user: true }, + }); + if (!session) throw new NotFoundException(SESSION_NOT_FOUND); + await this.prisma.session.updateMany({ + where: { token }, + data: { ipAddress, userAgent }, + }); + return { + accessToken: await this.getAccessToken(session.user, session.id), + refreshToken: token, + }; + } + + async logout(token: string): Promise { + if (!token) throw new UnprocessableEntityException(NO_TOKEN_PROVIDED); + const session = await this.prisma.session.findFirst({ + where: { token }, + select: { id: true, user: { select: { id: true } } }, + }); + if (!session) throw new NotFoundException(SESSION_NOT_FOUND); + await this.prisma.session.delete({ + where: { id: session.id }, + }); + } + + async approveSubnet( + ipAddress: string, + userAgent: string, + token: string, + ): Promise { + if (!token) throw new UnprocessableEntityException(NO_TOKEN_PROVIDED); + const { id } = this.tokensService.verify<{ id: number }>( + APPROVE_SUBNET_TOKEN, + token, + ); + const user = await this.prisma.user.findUnique({ where: { id } }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + await this.approvedSubnetsService.approveNewSubnet(id, ipAddress); + return this.loginResponse(ipAddress, userAgent, user); + } + + /** + * Get the two-factor authentication QR code + * @returns Data URI string with QR code image + */ + async getTotpQrCode(userId: number): Promise { + const secret = this.authenticator.generateSecret(); + await this.prisma.user.update({ + where: { id: userId }, + data: { twoFactorSecret: secret }, + }); + const otpauth = this.authenticator.keyuri( + userId.toString(), + this.configService.get('meta.appName') ?? '', + secret, + ); + return qrcode.toDataURL(otpauth); + } + + /** Enable two-factor authentication */ + async enableMfaMethod( + method: MfaMethod, + userId: number, + code: string, + ): Promise> { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { twoFactorSecret: true, twoFactorMethod: true }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (user.twoFactorMethod !== 'NONE') + throw new ConflictException(MFA_ENABLED_CONFLICT); + if (!user.twoFactorSecret) + user.twoFactorSecret = this.authenticator.generateSecret(); + if (!this.authenticator.check(code, user.twoFactorSecret)) + throw new UnauthorizedException(INVALID_MFA_CODE); + const result = await this.prisma.user.update({ + where: { id: userId }, + data: { twoFactorMethod: method, twoFactorSecret: user.twoFactorSecret }, + }); + return this.prisma.expose(result); + } + + async loginWithTotp( + ipAddress: string, + userAgent: string, + token: string, + code: string, + origin?: string, + ): Promise { + const { id } = this.tokensService.verify( + MULTI_FACTOR_TOKEN, + token, + ); + return this.loginUserWithTotpCode(ipAddress, userAgent, id, code, origin); + } + + async loginWithEmailToken( + ipAddress: string, + userAgent: string, + token: string, + ): Promise { + const { id } = this.tokensService.verify( + EMAIL_MFA_TOKEN, + token, + ); + const user = await this.prisma.user.findUnique({ where: { id } }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + await this.approvedSubnetsService.upsertNewSubnet(id, ipAddress); + return this.loginResponse(ipAddress, userAgent, user); + } + + async requestPasswordReset(email: string, origin?: string) { + const emailSafe = safeEmail(email); + const emailDetails = await this.prisma.email.findFirst({ + where: { emailSafe }, + include: { user: true }, + }); + if (!emailDetails) throw new NotFoundException(USER_NOT_FOUND); + this.email.send({ + to: `"${emailDetails.user.name}" <${email}>`, + template: 'auth/password-reset', + data: { + name: emailDetails.user.name, + minutes: 30, + link: `${ + origin ?? this.configService.get('frontendUrl') + }/auth/link/reset-password?token=${this.tokensService.signJwt( + PASSWORD_RESET_TOKEN, + { id: emailDetails.user.id }, + '30m', + )}`, + }, + }); + return { queued: true }; + } + + async resetPassword( + ipAddress: string, + userAgent: string, + token: string, + password: string, + ignorePwnedPassword?: boolean, + ): Promise { + const { id } = this.tokensService.verify<{ id: number }>( + PASSWORD_RESET_TOKEN, + token, + ); + const user = await this.prisma.user.findUnique({ + where: { id }, + include: { prefersEmail: true }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + password = await this.hashAndValidatePassword( + password, + !!ignorePwnedPassword, + ); + await this.prisma.user.update({ where: { id }, data: { password } }); + await this.approvedSubnetsService.upsertNewSubnet(id, ipAddress); + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'users/password-changed', + data: { + name: user.name, + }, + }); + return this.loginResponse(ipAddress, userAgent, user); + } + + async verifyEmail( + ipAddress: string, + userAgent: string, + token: string, + origin?: string, + ): Promise { + const { id } = this.tokensService.verify<{ id: number }>( + EMAIL_VERIFY_TOKEN, + token, + ); + const result = await this.prisma.email.update({ + where: { id }, + data: { isVerified: true }, + include: { user: true }, + }); + const groupsToJoin = await this.prisma.group.findMany({ + where: { + autoJoinDomain: true, + domains: { + some: { isVerified: true, domain: result.emailSafe.split('@')[1] }, + }, + }, + select: { id: true, name: true }, + }); + for await (const group of groupsToJoin) { + await this.prisma.membership.create({ + data: { + user: { connect: { id: result.user.id } }, + group: { connect: { id: group.id } }, + role: 'MEMBER', + }, + }); + this.email.send({ + to: `"${result.user.name}" <${result.email}>`, + template: 'groups/invitation', + data: { + name: result.user.name, + group: group.name, + link: `${ + origin ?? this.configService.get('frontendUrl') + }/groups/${group.id}`, + }, + }); + } + return this.loginResponse(ipAddress, userAgent, result.user); + } + + getOneTimePassword(secret: string): string { + return this.authenticator.generate(secret); + } + + private async loginUserWithTotpCode( + ipAddress: string, + userAgent: string, + id: number, + code: string, + origin?: string, + ): Promise { + const user = await this.prisma.user.findUnique({ + where: { id }, + include: { prefersEmail: true }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (user.twoFactorMethod === 'NONE' || !user.twoFactorSecret) + throw new BadRequestException(MFA_NOT_ENABLED); + if (this.authenticator.check(code, user.twoFactorSecret)) + return this.loginResponse(ipAddress, userAgent, user); + const backupCodes = await this.prisma.backupCode.findMany({ + where: { user: { id } }, + }); + let usedBackupCode = false; + for await (const backupCode of backupCodes) { + if (await compare(code, backupCode.code)) { + if (!usedBackupCode) { + if (backupCode.isUsed) + throw new UnauthorizedException(MFA_BACKUP_CODE_USED); + usedBackupCode = true; + await this.prisma.backupCode.update({ + where: { id: backupCode.id }, + data: { isUsed: true }, + }); + const location = await this.geolocationService.getLocation(ipAddress); + const locationName = + [ + location?.city?.names?.en, + (location?.subdivisions ?? [])[0]?.names?.en, + location?.country?.names?.en, + ] + .filter((i) => i) + .join(', ') || 'Unknown location'; + if (user.prefersEmail) + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'auth/used-backup-code', + data: { + name: user.name, + locationName, + link: `${ + origin ?? this.configService.get('frontendUrl') + }/users/${id}/sessions`, + }, + }); + } + } + } + if (!usedBackupCode) throw new UnauthorizedException(INVALID_MFA_CODE); + return this.loginResponse(ipAddress, userAgent, user); + } + + private async getAccessToken(user: User, sessionId: number): Promise { + const scopes = await this.getScopes(user); + const payload: AccessTokenClaims = { + id: user.id, + sessionId, + scopes, + role: user.role, + }; + return this.tokensService.signJwt( + LOGIN_ACCESS_TOKEN, + payload, + this.configService.get('security.accessTokenExpiry'), + ); + } + + private async loginResponse( + ipAddress: string, + userAgent: string, + user: User, + ): Promise { + const token = await this.tokensService.generateRandomString(64); + const ua = new UAParser(userAgent); + const location = await this.geolocationService.getLocation(ipAddress); + const { id } = await this.prisma.session.create({ + data: { + token, + ipAddress, + city: location?.city?.names?.en, + region: location?.subdivisions?.pop()?.names?.en, + timezone: location?.location?.time_zone, + countryCode: location?.country?.iso_code, + userAgent, + browser: + `${ua.getBrowser().name ?? ''} ${ + ua.getBrowser().version ?? '' + }`.trim() || undefined, + operatingSystem: + `${ua.getOS().name ?? ''} ${ua.getOS().version ?? ''}` + .replace('Mac OS', 'macOS') + .trim() || undefined, + user: { connect: { id: user.id } }, + }, + }); + return { + accessToken: await this.getAccessToken(user, id), + refreshToken: token, + }; + } + + private async mfaResponse( + user: User & { + prefersEmail: Email; + }, + forceMethod?: MfaMethod, + ): Promise { + const mfaTokenPayload: MfaTokenPayload = { + type: user.twoFactorMethod, + id: user.id, + }; + const totpToken = this.tokensService.signJwt( + MULTI_FACTOR_TOKEN, + mfaTokenPayload, + this.configService.get('security.mfaTokenExpiry'), + ); + if (user.twoFactorMethod === 'EMAIL' || forceMethod === 'EMAIL') { + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'auth/login-link', + data: { + name: user.name, + minutes: parseInt( + this.configService.get('security.mfaTokenExpiry') ?? '', + ), + link: `${this.configService.get( + 'frontendUrl', + )}/auth/link/login%2Ftoken?token=${this.tokensService.signJwt( + EMAIL_MFA_TOKEN, + { id: user.id }, + '30m', + )}`, + }, + }); + } else if (user.twoFactorMethod === 'SMS' || forceMethod === 'SMS') { + if (!user.twoFactorPhone) + throw new BadRequestException(MFA_PHONE_NOT_FOUND); + this.twilioService.send({ + to: user.twoFactorPhone, + body: `${this.getOneTimePassword(user.twoFactorSecret)} is your ${ + this.configService.get('meta.appName') ?? '' + } verification code.`, + }); + } + return { + totpToken, + type: forceMethod || user.twoFactorMethod, + multiFactorRequired: true, + }; + } + + private async checkLoginSubnet( + ipAddress: string, + _: string, // userAgent + checkLocationOnLogin: boolean, + id: number, + origin?: string, + ): Promise { + if (!checkLocationOnLogin) return; + const subnet = anonymize(ipAddress); + const previousSubnets = await this.prisma.approvedSubnet.findMany({ + where: { user: { id } }, + }); + let isApproved = false; + for await (const item of previousSubnets) { + if (!isApproved) + if (await compare(subnet, item.subnet)) isApproved = true; + } + if (!isApproved) { + const user = await this.prisma.user.findUnique({ + where: { id }, + select: { name: true, prefersEmail: true, checkLocationOnLogin: true }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (!user.checkLocationOnLogin) return; + const location = await this.geolocationService.getLocation(ipAddress); + const locationName = + [ + location?.city?.names?.en, + (location?.subdivisions ?? [])[0]?.names?.en, + location?.country?.names?.en, + ] + .filter((i) => i) + .join(', ') || 'Unknown location'; + if (user.prefersEmail) + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'auth/approve-subnet', + data: { + name: user.name, + locationName, + minutes: 30, + link: `${ + origin ?? this.configService.get('frontendUrl') + }/auth/link/approve-subnet?token=${this.tokensService.signJwt( + APPROVE_SUBNET_TOKEN, + { id }, + '30m', + )}`, + }, + }); + throw new UnauthorizedException(UNVERIFIED_LOCATION); + } + } + + async hashAndValidatePassword( + password: string, + ignorePwnedPassword: boolean, + ): Promise { + if (!ignorePwnedPassword) { + if (!this.configService.get('security.passwordPwnedCheck')) + return await hash( + password, + this.configService.get('security.saltRounds') ?? 10, + ); + if (!(await this.pwnedService.isPasswordSafe(password))) + throw new BadRequestException(COMPROMISED_PASSWORD); + } + return await hash( + password, + this.configService.get('security.saltRounds') ?? 10, + ); + } + + /** Get logging in scopes for a user */ + async getScopes(user: User): Promise { + // Superadmins can do anything + if (user.role === 'SUDO') return ['*']; + + // Add all scopes for user self + const scopes: string[] = Object.keys(userScopes).map((scope) => + scope.replace('{userId}', user.id.toString()), + ); + + // Add scopes for groups user is part of + const memberships = await this.prisma.membership.findMany({ + where: { user: { id: user.id } }, + select: { id: true, role: true, group: { select: { id: true } } }, + }); + for await (const membership of memberships) { + scopes.push(`membership-${membership.id}:*`); + const ids = [ + membership.group.id, + ...(await this.recursivelyGetSubgroupIds(membership.group.id)), + ]; + ids.forEach((id) => { + if (membership.role === 'OWNER') + scopes.push( + ...Object.keys(groupOwnerScopes).map((i) => + i.replace('{groupId}', id.toString()), + ), + ); + if (membership.role === 'ADMIN') + scopes.push( + ...Object.keys(groupAdminScopes).map((i) => + i.replace('{groupId}', id.toString()), + ), + ); + if (membership.role === 'MEMBER') + scopes.push( + ...Object.keys(groupMemberScopes).map((i) => + i.replace('{groupId}', id.toString()), + ), + ); + }); + } + return scopes; + } + + private async recursivelyGetSubgroupIds(groupId: number) { + const subgroups = await this.prisma.group.findMany({ + where: { parent: { id: groupId } }, + select: { + id: true, + parent: { select: { id: true } }, + subgroups: { select: { id: true } }, + }, + }); + const ids = subgroups.map((i) => i.id); + for await (const group of subgroups) { + for await (const subgroup of group.subgroups) { + const recurisiveIds = await this.recursivelyGetSubgroupIds(subgroup.id); + ids.push(...recurisiveIds); + } + } + return ids; + } + + async mergeUsers(token: string): Promise<{ success: true }> { + let baseUserId: number | undefined = undefined; + let mergeUserId: number | undefined = undefined; + try { + const result = this.tokensService.verify<{ + baseUserId: number; + mergeUserId: number; + }>(MERGE_ACCOUNTS_TOKEN, token); + baseUserId = result.baseUserId; + mergeUserId = result.mergeUserId; + } catch (error) {} + if (!baseUserId || !mergeUserId) + throw new BadRequestException(USER_NOT_FOUND); + return this.merge(baseUserId, mergeUserId); + } + + private async merge( + baseUserId: number, + mergeUserId: number, + ): Promise<{ success: true }> { + const baseUser = await this.prisma.user.findUnique({ + where: { id: baseUserId }, + }); + const mergeUser = await this.prisma.user.findUnique({ + where: { id: mergeUserId }, + }); + if (!baseUser || !mergeUser) throw new NotFoundException(USER_NOT_FOUND); + + const combinedUser: Record = {}; + [ + 'checkLocationOnLogin', + 'countryCode', + 'gender', + 'name', + 'notificationEmails', + 'active', + 'prefersLanguage', + 'prefersColorScheme', + 'prefersReducedMotion', + 'profilePictureUrl', + 'role', + 'timezone', + 'twoFactorMethod', + 'twoFactorPhone', + 'twoFactorSecret', + 'attributes', + ].forEach((key) => { + if (mergeUser[key] != null) combinedUser[key] = mergeUser[key]; + }); + await this.prisma.user.update({ + where: { id: baseUserId }, + data: combinedUser, + }); + + for await (const dataType of [ + this.prisma.membership as Prisma.EmailDelegate, + this.prisma.email as Prisma.EmailDelegate, + this.prisma.session as Prisma.EmailDelegate, + this.prisma.approvedSubnet as Prisma.EmailDelegate, + this.prisma.backupCode as Prisma.EmailDelegate, + this.prisma.identity as Prisma.EmailDelegate, + this.prisma.auditLog as Prisma.EmailDelegate, + this.prisma.apiKey as Prisma.EmailDelegate, + ] as Prisma.EmailDelegate[]) { + for await (const item of await (dataType as Prisma.EmailDelegate).findMany( + { + where: { user: { id: mergeUserId } }, + select: { id: true }, + }, + )) + await (dataType as Prisma.EmailDelegate).update({ + where: { id: item.id }, + data: { user: { connect: { id: baseUserId } } }, + }); + } + + await this.prisma.user.delete({ where: { id: mergeUser.id } }); + return { success: true }; + } +} diff --git a/src/modules/auth/public.decorator.ts b/src/modules/auth/public.decorator.ts new file mode 100644 index 00000000..5446e176 --- /dev/null +++ b/src/modules/auth/public.decorator.ts @@ -0,0 +1,16 @@ +import { STAART_PUBLIC_ENDPOINT } from './auth.constants'; + +export function Public() { + return ( + target: any, + _?: string | symbol, + descriptor?: TypedPropertyDescriptor, + ) => { + if (descriptor) { + Reflect.defineMetadata(STAART_PUBLIC_ENDPOINT, true, descriptor.value); + return descriptor; + } + Reflect.defineMetadata(STAART_PUBLIC_ENDPOINT, true, target); + return target; + }; +} diff --git a/src/modules/auth/rate-limit.decorator.ts b/src/modules/auth/rate-limit.decorator.ts new file mode 100644 index 00000000..0dd30218 --- /dev/null +++ b/src/modules/auth/rate-limit.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const RateLimit = (rateLimit: number) => + SetMetadata('rateLimit', rateLimit); diff --git a/src/modules/auth/scope.decorator.ts b/src/modules/auth/scope.decorator.ts new file mode 100644 index 00000000..437bffc6 --- /dev/null +++ b/src/modules/auth/scope.decorator.ts @@ -0,0 +1,3 @@ +import { SetMetadata } from '@nestjs/common'; + +export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes); diff --git a/src/modules/auth/scope.guard.ts b/src/modules/auth/scope.guard.ts new file mode 100644 index 00000000..9c3acd27 --- /dev/null +++ b/src/modules/auth/scope.guard.ts @@ -0,0 +1,27 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import minimatch from 'minimatch'; +import { AccessTokenParsed, UserRequest } from './auth.interface'; + +@Injectable() +export class ScopesGuard implements CanActivate { + constructor(private reflector: Reflector) {} + + canActivate(context: ExecutionContext): boolean { + const scopes = this.reflector.get('scopes', context.getHandler()); + const request = context.switchToHttp().getRequest(); + if (!scopes) return true; + const user: AccessTokenParsed = request.user; + let authorized = false; + if (!user) return false; + for (const userScope of user.scopes) { + for (let scope of scopes) { + for (const key in request.params) + scope = scope.replace(`{${key}}`, request.params[key]); + authorized = authorized || minimatch(scope, userScope); + if (authorized) return true; + } + } + return authorized; + } +} diff --git a/src/modules/auth/staart-auth.guard.ts b/src/modules/auth/staart-auth.guard.ts new file mode 100644 index 00000000..88fd51a4 --- /dev/null +++ b/src/modules/auth/staart-auth.guard.ts @@ -0,0 +1,19 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { STAART_PUBLIC_ENDPOINT } from './auth.constants'; + +@Injectable() +export class StaartAuthGuard extends AuthGuard('staart') { + constructor(private readonly reflector: Reflector) { + super(); + } + + public canActivate(context: ExecutionContext) { + const decoratorSkip = + this.reflector.get(STAART_PUBLIC_ENDPOINT, context.getClass()) || + this.reflector.get(STAART_PUBLIC_ENDPOINT, context.getHandler()); + if (decoratorSkip) return true; + return super.canActivate(context); + } +} diff --git a/src/modules/auth/staart.strategy.ts b/src/modules/auth/staart.strategy.ts new file mode 100644 index 00000000..9f8ca237 --- /dev/null +++ b/src/modules/auth/staart.strategy.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Request } from 'express'; +import ipRangeCheck from 'ip-range-check'; +import minimatch from 'minimatch'; +import { Strategy } from 'passport-strategy'; +import { getClientIp } from 'request-ip'; +import { ApiKeysService } from '../api-keys/api-keys.service'; +import { LOGIN_ACCESS_TOKEN } from '../../providers/tokens/tokens.constants'; +import { TokensService } from '../../providers/tokens/tokens.service'; +import { AccessTokenClaims, AccessTokenParsed } from './auth.interface'; + +class StaartStrategyName extends Strategy { + name = 'staart'; +} + +@Injectable() +export class StaartStrategy extends PassportStrategy(StaartStrategyName) { + constructor( + private apiKeyService: ApiKeysService, + private tokensService: TokensService, + ) { + super(); + } + + private safeSuccess(result: AccessTokenParsed) { + return this.success(result); + } + + async authenticate(request: Request) { + /** API key authorization */ + let authorizationKey = ''; + if (typeof request.query.api_key === 'string') + authorizationKey = request.query.api_key.replace('Bearer ', ''); + else if (typeof request.headers['x-api-key'] === 'string') + authorizationKey = request.headers['x-api-key'].replace('Bearer ', ''); + else if (request.headers.authorization) + authorizationKey = request.headers.authorization.replace('Bearer ', ''); + if (typeof authorizationKey === 'string') { + if (authorizationKey.startsWith('Bearer ')) + authorizationKey = authorizationKey.replace('Bearer ', ''); + if ( + // If authentication is *not* a JWT + !authorizationKey.match( + /^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$/, + ) + ) + try { + const apiKeyDetails = await this.apiKeyService.getApiKeyFromKey( + authorizationKey, + ); + const referer = request.headers.referer; + if (Array.isArray(apiKeyDetails.referrerRestrictions) && referer) { + let referrerRestrictionsMet = !apiKeyDetails.referrerRestrictions + .length; + apiKeyDetails.referrerRestrictions.forEach((restriction) => { + referrerRestrictionsMet = + referrerRestrictionsMet || + minimatch(referer, restriction as string); + }); + if (!referrerRestrictionsMet) + return this.fail('Referrer restrictions not met', 401); + } + if ( + Array.isArray(apiKeyDetails.ipRestrictions) && + apiKeyDetails.ipRestrictions.length + ) { + const ipAddress = getClientIp(request); + if ( + !ipRangeCheck(ipAddress, apiKeyDetails.ipRestrictions as string[]) + ) + return this.fail('IP address restrictions not met', 401); + } + return this.safeSuccess({ + type: 'api-key', + id: apiKeyDetails.id, + scopes: apiKeyDetails.scopes as string[], + }); + } catch (error) {} + } + + /** Bearer JWT authorization */ + let bearerToken = request.query['token'] ?? request.headers.authorization; + if (typeof bearerToken !== 'string') + return this.fail('No token found', 401); + if (bearerToken.startsWith('Bearer ')) + bearerToken = bearerToken.replace('Bearer ', ''); + try { + const payload = this.tokensService.verify( + LOGIN_ACCESS_TOKEN, + bearerToken, + ) as AccessTokenClaims; + const { id, scopes, sessionId, role } = payload; + return this.safeSuccess({ type: 'user', id, scopes, sessionId, role }); + } catch (error) {} + + return this.fail('Invalid token', 401); + } +} diff --git a/src/modules/domains/domains.constants.ts b/src/modules/domains/domains.constants.ts new file mode 100644 index 00000000..842894c6 --- /dev/null +++ b/src/modules/domains/domains.constants.ts @@ -0,0 +1,2 @@ +export const DOMAIN_VERIFICATION_TXT = 'DOMAIN_VERIFICATION_TXT'; +export const DOMAIN_VERIFICATION_HTML = 'DOMAIN_VERIFICATION_HTML'; diff --git a/src/modules/domains/domains.controller.ts b/src/modules/domains/domains.controller.ts new file mode 100644 index 00000000..93f0ead3 --- /dev/null +++ b/src/modules/domains/domains.controller.ts @@ -0,0 +1,111 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Post, + Query, +} from '@nestjs/common'; +import { Domain } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { + DOMAIN_VERIFICATION_HTML, + DOMAIN_VERIFICATION_TXT, +} from './domains.constants'; +import { CreateDomainDto } from './domains.dto'; +import { DomainsService } from './domains.service'; + +@Controller('groups/:groupId/domains') +export class DomainController { + constructor(private domainsService: DomainsService) {} + + /** Create a new domain for a group */ + @Post() + @AuditLog('create-domain') + @Scopes('group-{groupId}:write-domain-*') + async create( + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: CreateDomainDto, + ): Promise> { + return this.domainsService.createDomain(groupId, data); + } + + /** Get domains for a group */ + @Get() + @Scopes('group-{groupId}:read-domain-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.domainsService.getDomains(groupId, { + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Read a domain for a group */ + @Get(':id') + @Scopes('group-{groupId}:read-domain-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.domainsService.getDomain(groupId, id); + } + + /** Delete a domain for a group */ + @Delete(':id') + @AuditLog('delete-domain') + @Scopes('group-{groupId}:delete-domain-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.domainsService.deleteDomain(groupId, id); + } + + /** Verify a domain using TXT record */ + @Post(':id/verify/txt') + @AuditLog('verify-domain-txt') + @Scopes('group-{groupId}:write-domain-{id}') + async verifyTxt( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.domainsService.verifyDomain( + groupId, + id, + DOMAIN_VERIFICATION_TXT, + ); + } + + /** Verify a domain using HTML file upload */ + @Post(':id/verify/html') + @AuditLog('verify-domain-html') + @Scopes('group-{groupId}:write-domain-{id}') + async verifyHtml( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.domainsService.verifyDomain( + groupId, + id, + DOMAIN_VERIFICATION_HTML, + ); + } +} diff --git a/src/modules/domains/domains.dto.ts b/src/modules/domains/domains.dto.ts new file mode 100644 index 00000000..148cfbbb --- /dev/null +++ b/src/modules/domains/domains.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateDomainDto { + @IsString() + @IsNotEmpty() + domain: string; +} diff --git a/src/modules/domains/domains.interface.ts b/src/modules/domains/domains.interface.ts new file mode 100644 index 00000000..f4ce18aa --- /dev/null +++ b/src/modules/domains/domains.interface.ts @@ -0,0 +1,8 @@ +import { + DOMAIN_VERIFICATION_HTML, + DOMAIN_VERIFICATION_TXT, +} from './domains.constants'; + +export type DomainVerificationMethods = + | typeof DOMAIN_VERIFICATION_TXT + | typeof DOMAIN_VERIFICATION_HTML; diff --git a/src/modules/domains/domains.module.ts b/src/modules/domains/domains.module.ts new file mode 100644 index 00000000..18e3332c --- /dev/null +++ b/src/modules/domains/domains.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DnsModule } from '../../providers/dns/dns.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { DomainController } from './domains.controller'; +import { DomainsService } from './domains.service'; + +@Module({ + imports: [PrismaModule, TokensModule, DnsModule, ConfigModule], + controllers: [DomainController], + providers: [DomainsService], + exports: [DomainsService], +}) +export class DomainsModule {} diff --git a/src/modules/domains/domains.service.ts b/src/modules/domains/domains.service.ts new file mode 100644 index 00000000..711d2e38 --- /dev/null +++ b/src/modules/domains/domains.service.ts @@ -0,0 +1,171 @@ +import { + BadRequestException, + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { Prisma } from '@prisma/client'; +import { Domain } from '@prisma/client'; +import got from 'got'; +import { URL } from 'url'; +import { + DOMAIN_NOT_FOUND, + DOMAIN_NOT_VERIFIED, + INVALID_DOMAIN, + UNAUTHORIZED_RESOURCE, +} from '../../errors/errors.constants'; +import { DnsService } from '../../providers/dns/dns.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { TokensService } from '../../providers/tokens/tokens.service'; +import { + DOMAIN_VERIFICATION_HTML, + DOMAIN_VERIFICATION_TXT, +} from './domains.constants'; +import { DomainVerificationMethods } from './domains.interface'; + +@Injectable() +export class DomainsService { + constructor( + private prisma: PrismaService, + private tokensService: TokensService, + private dnsService: DnsService, + private configService: ConfigService, + ) {} + + async createDomain( + groupId: number, + data: Omit, 'verificationCode'>, + ): Promise { + try { + const fullUrl = new URL(data.domain); + data.domain = fullUrl.hostname; + } catch (error) {} + if ( + !/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test( + data.domain, + ) + ) + throw new BadRequestException(INVALID_DOMAIN); + const verificationCode = await this.tokensService.generateRandomString(); + const currentProfilePicture = await this.prisma.group.findUnique({ + where: { id: groupId }, + select: { profilePictureUrl: true }, + }); + const parsedProfilePicture = new URL( + currentProfilePicture.profilePictureUrl, + ); + if (parsedProfilePicture.hostname === 'ui-avatars.com') + try { + const img = await got('https://logo.clearbit.com/${data.domain}', { + responseType: 'buffer', + }); + if (img.body.byteLength > 1) + await this.prisma.group.update({ + where: { id: groupId }, + data: { + profilePictureUrl: `https://logo.clearbit.com/${data.domain}`, + }, + }); + } catch (error) {} + + return this.prisma.domain.create({ + data: { + ...data, + verificationCode, + group: { connect: { id: groupId } }, + }, + }); + } + + async getDomains( + groupId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.DomainWhereUniqueInput; + where?: Prisma.DomainWhereInput; + orderBy?: Prisma.DomainOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const domains = await this.prisma.domain.findMany({ + skip, + take, + cursor, + where: { ...where, group: { id: groupId } }, + orderBy, + }); + return domains.map((group) => this.prisma.expose(group)); + } catch (error) { + return []; + } + } + + async getDomain(groupId: number, id: number): Promise> { + const domain = await this.prisma.domain.findUnique({ + where: { id }, + }); + if (!domain) throw new NotFoundException(DOMAIN_NOT_FOUND); + if (domain.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(domain); + } + + async verifyDomain( + groupId: number, + id: number, + method?: DomainVerificationMethods, + ): Promise> { + const domain = await this.prisma.domain.findUnique({ + where: { id }, + }); + if (!domain) throw new NotFoundException(DOMAIN_NOT_FOUND); + if (domain.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + + if (method === DOMAIN_VERIFICATION_TXT || !method) { + const txtRecords = await this.dnsService.lookup(domain.domain, 'TXT'); + if (JSON.stringify(txtRecords).includes(domain.verificationCode)) { + await this.prisma.domain.update({ + where: { id }, + data: { isVerified: true }, + }); + } else if (method) throw new BadRequestException(DOMAIN_NOT_VERIFIED); + } + + if (method === DOMAIN_VERIFICATION_HTML || !method) { + let verified = false; + try { + const { body } = await got( + `http://${domain.domain}/.well-known/${this.configService.get( + 'meta.domainVerificationFile' ?? 'staart-verify.txt', + )}`, + ); + verified = body.includes(domain.verificationCode); + } catch (error) {} + if (verified) { + await this.prisma.domain.update({ + where: { id }, + data: { isVerified: true }, + }); + } else if (method) throw new BadRequestException(DOMAIN_NOT_VERIFIED); + } + return domain; + } + + async deleteDomain(groupId: number, id: number): Promise> { + const testDomain = await this.prisma.domain.findUnique({ + where: { id }, + }); + if (!testDomain) throw new NotFoundException(DOMAIN_NOT_FOUND); + if (testDomain.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const domain = await this.prisma.domain.delete({ + where: { id }, + }); + return this.prisma.expose(domain); + } +} diff --git a/src/modules/emails/emails.controller.ts b/src/modules/emails/emails.controller.ts new file mode 100644 index 00000000..5dc9e9b1 --- /dev/null +++ b/src/modules/emails/emails.controller.ts @@ -0,0 +1,74 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Post, + Query, +} from '@nestjs/common'; +import { Email } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { CreateEmailDto } from './emails.dto'; +import { EmailsService } from './emails.service'; + +@Controller('users/:userId/emails') +export class EmailController { + constructor(private emailsService: EmailsService) {} + + /** Create a new email for a user */ + @Post() + @Scopes('user-{userId}:write-email-*') + async create( + @Param('userId', ParseIntPipe) userId: number, + @Body() data: CreateEmailDto, + ): Promise> { + return this.emailsService.createEmail(userId, data); + } + + /** Get emails for a user */ + @Get() + @Scopes('user-{userId}:read-email-*') + async getAll( + @Param('userId', ParseIntPipe) userId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.emailsService.getEmails(userId, { + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Get an email for a user */ + @Get(':id') + @Scopes('user-{userId}:read-email-{id}') + async get( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.emailsService.getEmail(userId, id); + } + + /** Delete an email for a user */ + @Delete(':id') + @Scopes('user-{userId}:delete-email-{id}') + async remove( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.emailsService.deleteEmail(userId, id); + } +} diff --git a/src/modules/emails/emails.dto.ts b/src/modules/emails/emails.dto.ts new file mode 100644 index 00000000..67b3d2f9 --- /dev/null +++ b/src/modules/emails/emails.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsEmail } from 'class-validator'; + +export class CreateEmailDto { + @IsEmail() + @IsNotEmpty() + email!: string; +} diff --git a/src/modules/emails/emails.module.ts b/src/modules/emails/emails.module.ts new file mode 100644 index 00000000..5d74d420 --- /dev/null +++ b/src/modules/emails/emails.module.ts @@ -0,0 +1,37 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ApprovedSubnetsService } from '../approved-subnets/approved-subnets.service'; +import { AuthService } from '../auth/auth.service'; +import { MailModule } from '../../providers/mail/mail.module'; +import { GeolocationService } from '../../providers/geolocation/geolocation.service'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { PwnedModule } from '../../providers/pwned/pwned.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { TwilioModule } from '../../providers/twilio/twilio.module'; +import { UsersService } from '../users/users.service'; +import { EmailController } from './emails.controller'; +import { EmailsService } from './emails.service'; +import { S3Module } from '../../providers/s3/s3.module'; +import { ApiKeysModule } from '../api-keys/api-keys.module'; + +@Module({ + imports: [ + PrismaModule, + MailModule, + ConfigModule, + TwilioModule, + PwnedModule, + TokensModule, + S3Module, + ApiKeysModule, + ], + controllers: [EmailController], + providers: [ + EmailsService, + UsersService, + AuthService, + GeolocationService, + ApprovedSubnetsService, + ], +}) +export class EmailsModule {} diff --git a/src/modules/emails/emails.service.ts b/src/modules/emails/emails.service.ts new file mode 100644 index 00000000..085d0c6b --- /dev/null +++ b/src/modules/emails/emails.service.ts @@ -0,0 +1,104 @@ +import { + BadRequestException, + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import type { Prisma } from '@prisma/client'; +import { Email } from '@prisma/client'; +import { + EMAIL_DELETE_PRIMARY, + EMAIL_NOT_FOUND, + UNAUTHORIZED_RESOURCE, + USER_NOT_FOUND, +} from '../../errors/errors.constants'; +import { safeEmail } from '../../helpers/safe-email'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { AuthService } from '../auth/auth.service'; +import { UsersService } from '../users/users.service'; + +@Injectable() +export class EmailsService { + constructor( + private prisma: PrismaService, + private users: UsersService, + private auth: AuthService, + ) {} + + async createEmail( + userId: number, + data: Omit, 'user'>, + ): Promise { + const emailSafe = safeEmail(data.email); + const result = await this.prisma.email.create({ + data: { ...data, emailSafe, user: { connect: { id: userId } } }, + }); + await this.auth.sendEmailVerification(data.email); + return result; + } + + async getEmails( + userId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.EmailWhereUniqueInput; + where?: Prisma.EmailWhereInput; + orderBy?: Prisma.EmailOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const emails = await this.prisma.email.findMany({ + skip, + take, + cursor, + where: { ...where, user: { id: userId } }, + orderBy, + }); + return emails.map((user) => this.prisma.expose(user)); + } catch (error) { + return []; + } + } + + async getEmail(userId: number, id: number): Promise> { + const email = await this.prisma.email.findUnique({ + where: { id }, + }); + if (!email) throw new NotFoundException(EMAIL_NOT_FOUND); + if (email.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(email); + } + + async deleteEmail(userId: number, id: number): Promise> { + const testEmail = await this.prisma.email.findUnique({ + where: { id }, + }); + if (!testEmail) throw new NotFoundException(EMAIL_NOT_FOUND); + if (testEmail.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + include: { prefersEmail: true }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (user.prefersEmail.id === id) { + const otherEmails = ( + await this.prisma.email.findMany({ where: { user: { id: userId } } }) + ).filter((i) => i.id !== id); + if (!otherEmails.length) + throw new BadRequestException(EMAIL_DELETE_PRIMARY); + await this.prisma.user.update({ + where: { id: userId }, + data: { prefersEmail: { connect: { id: otherEmails[0].id } } }, + }); + } + const email = await this.prisma.email.delete({ + where: { id }, + }); + return this.prisma.expose(email); + } +} diff --git a/src/modules/groups/groups.controller.ts b/src/modules/groups/groups.controller.ts new file mode 100644 index 00000000..64fb9c27 --- /dev/null +++ b/src/modules/groups/groups.controller.ts @@ -0,0 +1,109 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Put, + Query, +} from '@nestjs/common'; +import { Group } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { SelectIncludePipe } from '../../pipes/select-include.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { ReplaceGroupDto, UpdateGroupDto } from './groups.dto'; +import { GroupsService } from './groups.service'; + +@Controller('groups') +export class GroupController { + constructor(private groupsService: GroupsService) {} + + /** Get groups */ + @Get() + @Scopes('group-*:read-info') + async getAll( + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.groupsService.getGroups({ + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Get group details */ + @Get(':groupId') + @Scopes('group-{groupId}:read-info') + async get( + @Param('groupId', ParseIntPipe) id: number, + @Query('select', SelectIncludePipe) select?: Record, + @Query('include', SelectIncludePipe) include?: Record, + ): Promise> { + return this.groupsService.getGroup(id, { select, include }); + } + + /** Update a group */ + @Patch(':groupId') + @AuditLog('update-info') + @Scopes('group-{groupId}:write-info') + async update( + @Body() data: UpdateGroupDto, + @Param('groupId', ParseIntPipe) id: number, + ): Promise> { + return this.groupsService.updateGroup(id, data); + } + + /** Replace a group */ + @Put(':groupId') + @AuditLog('update-info') + @Scopes('group-{groupId}:write-info') + async replace( + @Body() data: ReplaceGroupDto, + @Param('groupId', ParseIntPipe) id: number, + ): Promise> { + return this.groupsService.updateGroup(id, data); + } + + /** Delete a group */ + @Delete(':groupId') + @AuditLog('delete') + @Scopes('group-{groupId}:delete') + async remove( + @Param('groupId', ParseIntPipe) id: number, + ): Promise> { + return this.groupsService.deleteGroup(id); + } + + /** Get subgroups for a group */ + @Get(':groupId/subgroups') + @Scopes('group-*:read-info') + async getSubgroups( + @Param('groupId', ParseIntPipe) id: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.groupsService.getSubgroups(id, { + skip, + take, + orderBy, + cursor, + where, + }); + } +} diff --git a/src/modules/groups/groups.dto.ts b/src/modules/groups/groups.dto.ts new file mode 100644 index 00000000..d189518e --- /dev/null +++ b/src/modules/groups/groups.dto.ts @@ -0,0 +1,90 @@ +import { + IsArray, + IsBoolean, + IsNotEmpty, + IsObject, + IsOptional, + IsString, +} from 'class-validator'; + +export class CreateGroupDto { + @IsBoolean() + @IsOptional() + autoJoinDomain?: boolean; + + @IsBoolean() + @IsOptional() + forceTwoFactor?: boolean; + + @IsArray() + @IsOptional() + ipRestrictions?: string; + + @IsString() + @IsNotEmpty() + name!: string; + + @IsBoolean() + @IsOptional() + onlyAllowDomain?: boolean; + + @IsString() + @IsOptional() + profilePictureUrl?: string; +} + +export class UpdateGroupDto { + @IsBoolean() + @IsOptional() + autoJoinDomain?: boolean; + + @IsBoolean() + @IsOptional() + forceTwoFactor?: boolean; + + @IsArray() + @IsOptional() + ipRestrictions?: string; + + @IsString() + @IsOptional() + name?: string; + + @IsBoolean() + @IsOptional() + onlyAllowDomain?: boolean; + + @IsString() + @IsOptional() + profilePictureUrl?: string; +} + +export class ReplaceGroupDto { + @IsBoolean() + @IsNotEmpty() + autoJoinDomain!: boolean; + + @IsBoolean() + @IsNotEmpty() + forceTwoFactor!: boolean; + + @IsArray() + @IsNotEmpty() + ipRestrictions!: string; + + @IsString() + @IsNotEmpty() + name!: string; + + @IsBoolean() + @IsNotEmpty() + onlyAllowDomain!: boolean; + + @IsString() + @IsNotEmpty() + profilePictureUrl!: string; + + @IsObject() + @IsNotEmpty() + attributes!: Record; +} diff --git a/src/modules/groups/groups.module.ts b/src/modules/groups/groups.module.ts new file mode 100644 index 00000000..54f18fb6 --- /dev/null +++ b/src/modules/groups/groups.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { GroupController } from './groups.controller'; +import { GroupsService } from './groups.service'; + +@Module({ + imports: [PrismaModule], + controllers: [GroupController], + providers: [GroupsService], + exports: [GroupsService], +}) +export class GroupsModule {} diff --git a/src/modules/groups/groups.service.ts b/src/modules/groups/groups.service.ts new file mode 100644 index 00000000..d96ae009 --- /dev/null +++ b/src/modules/groups/groups.service.ts @@ -0,0 +1,147 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import type { Prisma } from '@prisma/client'; +import { Group } from '@prisma/client'; +import randomColor from 'randomcolor'; +import { GROUP_NOT_FOUND } from '../../errors/errors.constants'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; + +@Injectable() +export class GroupsService { + constructor(private prisma: PrismaService) {} + + async createGroup( + userId: number, + data: Omit, 'user'>, + ) { + let initials = data.name.trim().substr(0, 2).toUpperCase(); + if (data.name.includes(' ')) + initials = data.name + .split(' ') + .map((i) => i.trim().substr(0, 1)) + .join('') + .toUpperCase(); + data.profilePictureUrl = + data.profilePictureUrl ?? + `https://ui-avatars.com/api/?name=${initials}&background=${randomColor({ + luminosity: 'light', + }).replace('#', '')}&color=000000`; + return this.prisma.group.create({ + include: { memberships: { include: { group: true } } }, + data: { + ...data, + memberships: { + create: { role: 'OWNER', user: { connect: { id: userId } } }, + }, + }, + }); + } + + async getGroups(params: { + skip?: number; + take?: number; + cursor?: Prisma.GroupWhereUniqueInput; + where?: Prisma.GroupWhereInput; + orderBy?: Prisma.GroupOrderByInput; + }): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const groups = await this.prisma.group.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + return groups.map((user) => this.prisma.expose(user)); + } catch (error) { + return []; + } + } + + async getGroup( + id: number, + { + select, + include, + }: { + select?: Record; + include?: Record; + }, + ): Promise> { + const group = await this.prisma.group.findUnique({ + where: { id }, + select, + include, + } as any); + if (!group) throw new NotFoundException(GROUP_NOT_FOUND); + return this.prisma.expose(group); + } + + async updateGroup( + id: number, + data: Prisma.GroupUpdateInput, + ): Promise> { + const testGroup = await this.prisma.group.findUnique({ + where: { id }, + }); + if (!testGroup) throw new NotFoundException(GROUP_NOT_FOUND); + const group = await this.prisma.group.update({ + where: { id }, + data, + }); + return this.prisma.expose(group); + } + + async replaceGroup( + id: number, + data: Prisma.GroupCreateInput, + ): Promise> { + const testGroup = await this.prisma.group.findUnique({ + where: { id }, + }); + if (!testGroup) throw new NotFoundException(GROUP_NOT_FOUND); + const group = await this.prisma.group.update({ + where: { id }, + data, + }); + return this.prisma.expose(group); + } + + async deleteGroup(id: number): Promise> { + const testGroup = await this.prisma.group.findUnique({ + where: { id }, + }); + if (!testGroup) throw new NotFoundException(GROUP_NOT_FOUND); + await this.prisma.membership.deleteMany({ where: { group: { id } } }); + const group = await this.prisma.group.delete({ + where: { id }, + }); + return this.prisma.expose(group); + } + + async getSubgroups( + id: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.GroupWhereUniqueInput; + where?: Prisma.GroupWhereInput; + orderBy?: Prisma.GroupOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const groups = await this.prisma.group.findMany({ + skip, + take, + cursor, + where: { ...where, parent: { id } }, + orderBy, + }); + return groups.map((user) => this.prisma.expose(user)); + } catch (error) { + return []; + } + } +} diff --git a/src/modules/memberships/memberships-group.controller.ts b/src/modules/memberships/memberships-group.controller.ts new file mode 100644 index 00000000..f1fc48c4 --- /dev/null +++ b/src/modules/memberships/memberships-group.controller.ts @@ -0,0 +1,95 @@ +import { + Body, + Controller, + Delete, + Get, + Ip, + Param, + ParseIntPipe, + Patch, + Post, + Query, +} from '@nestjs/common'; +import { Membership } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { + CreateGroupMembershipDto, + UpdateMembershipDto, +} from './memberships.dto'; +import { MembershipsService } from './memberships.service'; + +@Controller('groups/:groupId/memberships') +export class GroupMembershipController { + constructor(private membershipsService: MembershipsService) {} + + /** Add a member to a group */ + @Post() + @AuditLog('add-membership') + @Scopes('group-{groupId}:write-membership-*') + async create( + @Ip() ip: string, + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: CreateGroupMembershipDto, + ): Promise> { + return this.membershipsService.createGroupMembership(ip, groupId, data); + } + + /** Get memberships for a group */ + @Get() + @Scopes('group-{groupId}:read-membership-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.membershipsService.getMemberships({ + skip, + take, + orderBy, + cursor, + where: { ...where, group: { id: groupId } }, + }); + } + + /** Get a membership for a group */ + @Get(':id') + @Scopes('group-{groupId}:read-membership-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.getGroupMembership(groupId, id); + } + + /** Update a membership for a group */ + @Patch(':id') + @AuditLog('update-membership') + @Scopes('group-{groupId}:write-membership-{id}') + async update( + @Body() data: UpdateMembershipDto, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.updateGroupMembership(groupId, id, data); + } + + /** Remove a member from a group */ + @Delete(':id') + @AuditLog('delete-membership') + @Scopes('group-{groupId}:delete-membership-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.deleteGroupMembership(groupId, id); + } +} diff --git a/src/modules/memberships/memberships-user.controller.ts b/src/modules/memberships/memberships-user.controller.ts new file mode 100644 index 00000000..b0dbf027 --- /dev/null +++ b/src/modules/memberships/memberships-user.controller.ts @@ -0,0 +1,73 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Post, + Query, +} from '@nestjs/common'; +import { Membership } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { CreateGroupDto } from '../groups/groups.dto'; +import { MembershipsService } from './memberships.service'; + +@Controller('users/:userId/memberships') +export class UserMembershipController { + constructor(private membershipsService: MembershipsService) {} + + @Post() + @Scopes('user-{userId}:write-membership-*') + async create( + @Param('userId', ParseIntPipe) userId: number, + @Body() data: CreateGroupDto, + ): Promise> { + return this.membershipsService.createUserMembership(userId, data); + } + + /** Get memberships for a user */ + @Get() + @Scopes('user-{userId}:read-membership-*') + async getAll( + @Param('userId', ParseIntPipe) userId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.membershipsService.getMemberships({ + skip, + take, + orderBy, + cursor, + where: { ...where, user: { id: userId } }, + }); + } + + /** Get a membership for a user */ + @Get(':id') + @Scopes('user-{userId}:read-membership-{id}') + async get( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.getUserMembership(userId, id); + } + + /** Delete a membership for a user */ + @Delete(':id') + @Scopes('user-{userId}:delete-membership-{id}') + async remove( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.deleteUserMembership(userId, id); + } +} diff --git a/src/modules/memberships/memberships.dto.ts b/src/modules/memberships/memberships.dto.ts new file mode 100644 index 00000000..b4af63f1 --- /dev/null +++ b/src/modules/memberships/memberships.dto.ts @@ -0,0 +1,31 @@ +import { + IsEmail, + IsIn, + IsNotEmpty, + IsOptional, + IsString, + MinLength, +} from 'class-validator'; + +export class UpdateMembershipDto { + @IsString() + @IsIn(['OWNER', 'ADMIN', 'MEMBER']) + @IsOptional() + role?: 'OWNER' | 'ADMIN' | 'MEMBER'; +} + +export class CreateGroupMembershipDto { + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsString() + @IsOptional() + @MinLength(3) + name?: string; + + @IsString() + @IsIn(['OWNER', 'ADMIN', 'MEMBER']) + @IsOptional() + role?: 'OWNER' | 'ADMIN' | 'MEMBER'; +} diff --git a/src/modules/memberships/memberships.interface.ts b/src/modules/memberships/memberships.interface.ts new file mode 100644 index 00000000..1f2a7d3f --- /dev/null +++ b/src/modules/memberships/memberships.interface.ts @@ -0,0 +1,7 @@ +import { MembershipRole } from '@prisma/client'; + +export interface CreateMembershipInput { + email: string; + name?: string; + role?: MembershipRole; +} diff --git a/src/modules/memberships/memberships.module.ts b/src/modules/memberships/memberships.module.ts new file mode 100644 index 00000000..349cf1a4 --- /dev/null +++ b/src/modules/memberships/memberships.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { MailModule } from '../../providers/mail/mail.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { ApiKeysModule } from '../api-keys/api-keys.module'; +import { AuthModule } from '../auth/auth.module'; +import { GroupsModule } from '../groups/groups.module'; +import { GroupMembershipController } from './memberships-group.controller'; +import { UserMembershipController } from './memberships-user.controller'; +import { MembershipsService } from './memberships.service'; + +@Module({ + imports: [ + PrismaModule, + MailModule, + ConfigModule, + AuthModule, + GroupsModule, + ApiKeysModule, + TokensModule, + ], + controllers: [UserMembershipController, GroupMembershipController], + providers: [MembershipsService], +}) +export class MembershipsModule {} diff --git a/src/modules/memberships/memberships.service.ts b/src/modules/memberships/memberships.service.ts new file mode 100644 index 00000000..dddf6764 --- /dev/null +++ b/src/modules/memberships/memberships.service.ts @@ -0,0 +1,235 @@ +import { + BadRequestException, + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Membership, User } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; +import { + CANNOT_DELETE_SOLE_MEMBER, + CANNOT_DELETE_SOLE_OWNER, + CANNOT_UPDATE_ROLE_SOLE_OWNER, + MEMBERSHIP_NOT_FOUND, + UNAUTHORIZED_RESOURCE, +} from '../../errors/errors.constants'; +import { safeEmail } from '../../helpers/safe-email'; +import { MailService } from '../../providers/mail/mail.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { ApiKeysService } from '../api-keys/api-keys.service'; +import { AuthService } from '../auth/auth.service'; +import { GroupsService } from '../groups/groups.service'; +import { CreateMembershipInput } from './memberships.interface'; +import { TokensService } from '../../providers/tokens/tokens.service'; + +@Injectable() +export class MembershipsService { + constructor( + private prisma: PrismaService, + private auth: AuthService, + private email: MailService, + private configService: ConfigService, + private groupsService: GroupsService, + private apiKeyService: ApiKeysService, + private tokensService: TokensService, + ) {} + + async createUserMembership( + userId: number, + data: Omit, + ) { + let id: number | undefined = undefined; + while (!id) { + id = Number( + `10${await this.tokensService.generateRandomString(6, 'numeric')}`, + ); + const users = await this.prisma.user.findMany({ where: { id }, take: 1 }); + if (users.length) id = undefined; + } + const created = await this.groupsService.createGroup(userId, { + ...data, + id, + }); + return created.memberships[0]; + } + + async getMemberships(params: { + skip?: number; + take?: number; + cursor?: Prisma.MembershipWhereUniqueInput; + where?: Prisma.MembershipWhereInput; + orderBy?: Prisma.MembershipOrderByInput; + }): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const memberships = await this.prisma.membership.findMany({ + skip, + take, + cursor, + where, + orderBy, + include: { group: true, user: true }, + }); + return memberships.map((user) => this.prisma.expose(user)); + } catch (error) { + return []; + } + } + + async getUserMembership( + userId: number, + id: number, + ): Promise> { + const membership = await this.prisma.membership.findUnique({ + where: { id }, + include: { group: true }, + }); + if (!membership) throw new NotFoundException(MEMBERSHIP_NOT_FOUND); + if (membership.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(membership); + } + + async getGroupMembership( + groupId: number, + id: number, + ): Promise> { + const membership = await this.prisma.membership.findUnique({ + where: { id }, + include: { user: true }, + }); + if (!membership) throw new NotFoundException(MEMBERSHIP_NOT_FOUND); + if (membership.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(membership); + } + + async deleteUserMembership( + userId: number, + id: number, + ): Promise> { + const testMembership = await this.prisma.membership.findUnique({ + where: { id }, + }); + if (!testMembership) throw new NotFoundException(MEMBERSHIP_NOT_FOUND); + if (testMembership.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + await this.verifyDeleteMembership(testMembership.groupId, id); + const membership = await this.prisma.membership.delete({ + where: { id }, + }); + await this.apiKeyService.removeUnauthorizedScopesForUser(userId); + return this.prisma.expose(membership); + } + + async updateGroupMembership( + groupId: number, + id: number, + data: Prisma.MembershipUpdateInput, + ): Promise> { + const testMembership = await this.prisma.membership.findUnique({ + where: { id }, + }); + if (!testMembership) throw new NotFoundException(MEMBERSHIP_NOT_FOUND); + if (testMembership.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + if (testMembership.role === 'OWNER' && data.role !== 'OWNER') { + const otherOwners = ( + await this.prisma.membership.findMany({ + where: { group: { id: groupId }, role: 'OWNER' }, + }) + ).filter((i) => i.id !== id); + if (!otherOwners.length) + throw new BadRequestException(CANNOT_UPDATE_ROLE_SOLE_OWNER); + } + const membership = await this.prisma.membership.update({ + where: { id }, + data, + include: { user: true }, + }); + await this.apiKeyService.removeUnauthorizedScopesForUser( + testMembership.userId, + ); + return this.prisma.expose(membership); + } + + async deleteGroupMembership( + groupId: number, + id: number, + ): Promise> { + const testMembership = await this.prisma.membership.findUnique({ + where: { id }, + }); + if (!testMembership) throw new NotFoundException(MEMBERSHIP_NOT_FOUND); + if (testMembership.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + await this.verifyDeleteMembership(testMembership.groupId, id); + const membership = await this.prisma.membership.delete({ + where: { id }, + include: { user: true }, + }); + await this.apiKeyService.removeUnauthorizedScopesForUser( + testMembership.userId, + ); + return this.prisma.expose(membership); + } + + async createGroupMembership( + ipAddress: string, + groupId: number, + data: CreateMembershipInput, + ) { + const emailSafe = safeEmail(data.email); + const userResult = await this.prisma.user.findFirst({ + where: { emails: { some: { emailSafe } } }, + }); + let user: Expose | null = userResult + ? this.prisma.expose(userResult) + : null; + if (!user) + user = await this.auth.register(ipAddress, { name: data.email, ...data }); + const result = await this.prisma.membership.create({ + data: { + role: data.role, + group: { connect: { id: groupId } }, + user: { connect: { id: user.id } }, + }, + include: { group: { select: { name: true } } }, + }); + this.email.send({ + to: `"${user.name}" <${data.email}>`, + template: 'groups/invitation', + data: { + name: user.name, + group: result.group.name, + link: `${this.configService.get( + 'frontendUrl', + )}/groups/${groupId}`, + }, + }); + return this.prisma.expose(result); + } + + /** Verify whether a group membership can be deleted */ + private async verifyDeleteMembership( + groupId: number, + membershipId: number, + ): Promise { + const memberships = await this.prisma.membership.findMany({ + where: { group: { id: groupId } }, + }); + if (memberships.length === 1) + throw new BadRequestException(CANNOT_DELETE_SOLE_MEMBER); + const membership = await this.prisma.membership.findUnique({ + where: { id: membershipId }, + }); + if (!membership) throw new NotFoundException(MEMBERSHIP_NOT_FOUND); + if ( + membership.role === 'OWNER' && + memberships.filter((i) => i.role === 'OWNER').length === 1 + ) + throw new BadRequestException(CANNOT_DELETE_SOLE_OWNER); + } +} diff --git a/src/modules/meta/meta.controller.ts b/src/modules/meta/meta.controller.ts new file mode 100644 index 00000000..acc1ec1a --- /dev/null +++ b/src/modules/meta/meta.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get, HttpStatus, Redirect } from '@nestjs/common'; +import { Public } from '../auth/public.decorator'; + +@Controller() +@Public() +export class MetaController { + constructor() {} + + /** Redirect to staart/staart */ + @Get() + @Redirect('https://github.com/staart/staart', HttpStatus.FOUND) + get() {} +} diff --git a/src/modules/meta/meta.module.ts b/src/modules/meta/meta.module.ts new file mode 100644 index 00000000..52fb01b8 --- /dev/null +++ b/src/modules/meta/meta.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { MetaController } from './meta.controller'; + +@Module({ + controllers: [MetaController], +}) +export class MetaModule {} diff --git a/src/modules/metrics/metrics.controller.ts b/src/modules/metrics/metrics.controller.ts new file mode 100644 index 00000000..37facce6 --- /dev/null +++ b/src/modules/metrics/metrics.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Post } from '@nestjs/common'; +import { Scopes } from '../auth/scope.decorator'; +import { ProcessMetricData } from './metrics.interface'; +import { MetricsService } from './metrics.service'; + +@Controller('metrics') +export class MetricsController { + constructor(private metricsService: MetricsService) {} + + /** Get process metrics */ + @Get('process') + @Scopes('metric:read-process') + process(): Promise { + return this.metricsService.getProcessMetrics(); + } + + /** Update metrics */ + @Post('update') + @Scopes('metric:write-process') + update(): Promise { + return this.metricsService.updateProcessMetrics(); + } +} diff --git a/src/modules/metrics/metrics.interface.ts b/src/modules/metrics/metrics.interface.ts new file mode 100644 index 00000000..f19754fb --- /dev/null +++ b/src/modules/metrics/metrics.interface.ts @@ -0,0 +1,5 @@ +export interface ProcessMetricData { + date: Date; + cpu: number; + memory: number; +} diff --git a/src/modules/metrics/metrics.module.ts b/src/modules/metrics/metrics.module.ts new file mode 100644 index 00000000..a75becdd --- /dev/null +++ b/src/modules/metrics/metrics.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { MetricsController } from './metrics.controller'; +import { MetricsService } from './metrics.service'; + +@Module({ + imports: [PrismaModule], + controllers: [MetricsController], + providers: [MetricsService], + exports: [MetricsService], +}) +export class MetricsModule {} diff --git a/src/modules/metrics/metrics.service.ts b/src/modules/metrics/metrics.service.ts new file mode 100644 index 00000000..d9986a3b --- /dev/null +++ b/src/modules/metrics/metrics.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import CircularBuffer from 'circularbuffer'; +import pidusage from 'pidusage'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import type { ProcessMetricData } from './metrics.interface'; + +@Injectable() +export class MetricsService { + private queue = new CircularBuffer(60); + + constructor(private prisma: PrismaService) {} + + async updateProcessMetrics(): Promise { + const stats = await pidusage(process.pid); + this.queue.enq({ + date: new Date(), + cpu: Math.round(stats.cpu * 100) / 100, + memory: Math.round(stats.memory * 100) / 100, + }); + return this.getProcessMetrics(); + } + + async getProcessMetrics(): Promise { + return this.queue.toArray(); + } +} diff --git a/src/modules/multi-factor-authentication/multi-factor-authentication.controller.ts b/src/modules/multi-factor-authentication/multi-factor-authentication.controller.ts new file mode 100644 index 00000000..225f7d0d --- /dev/null +++ b/src/modules/multi-factor-authentication/multi-factor-authentication.controller.ts @@ -0,0 +1,101 @@ +import { + BadRequestException, + Body, + Controller, + Delete, + Param, + ParseIntPipe, + Post, +} from '@nestjs/common'; +import { User } from '@prisma/client'; +import { MFA_PHONE_OR_TOKEN_REQUIRED } from '../../errors/errors.constants'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { + EnableSmsMfaDto, + EnableTotpMfaDto, +} from './multi-factor-authentication.dto'; +import { MultiFactorAuthenticationService } from './multi-factor-authentication.service'; + +@Controller('users/:userId/multi-factor-authentication') +export class MultiFactorAuthenticationController { + constructor( + private multiFactorAuthenticationService: MultiFactorAuthenticationService, + ) {} + + /** Disable MFA for a user */ + @Delete() + @Scopes('user-{userId}:delete-mfa-*') + async disable2FA( + @Param('userId', ParseIntPipe) userId: number, + ): Promise> { + return this.multiFactorAuthenticationService.disableMfa(userId); + } + + /** Regenerate backup codes for a user */ + @Post('regenerate') + @Scopes('user-{userId}:write-mfa-regenerate') + async regenerateBackupCodes( + @Param('userId', ParseIntPipe) userId: number, + ): Promise { + return this.multiFactorAuthenticationService.regenerateBackupCodes(userId); + } + + /** Enable TOTP-based MFA for a user */ + @Post('totp') + @Scopes('user-{userId}:write-mfa-totp') + async enableTotp( + @Param('userId', ParseIntPipe) userId: number, + @Body() body: EnableTotpMfaDto, + ): Promise { + if (body.token) + return this.multiFactorAuthenticationService.enableMfa( + 'TOTP', + userId, + body.token, + ); + return { + img: await this.multiFactorAuthenticationService.requestTotpMfa(userId), + }; + } + + /** Enable SMS-based MFA for a user */ + @Post('sms') + @Scopes('user-{userId}:write-mfa-sms') + async enableSms( + @Param('userId', ParseIntPipe) userId: number, + @Body() body: EnableSmsMfaDto, + ): Promise { + if (body.token) + return this.multiFactorAuthenticationService.enableMfa( + 'SMS', + userId, + body.token, + ); + if (body.phone) { + await this.multiFactorAuthenticationService.requestSmsMfa( + userId, + body.phone, + ); + return { success: true }; + } + throw new BadRequestException(MFA_PHONE_OR_TOKEN_REQUIRED); + } + + /** Enable email-based MFA for a user */ + @Post('email') + @Scopes('user-{userId}:write-mfa-email') + async enableEmail( + @Param('userId', ParseIntPipe) userId: number, + @Body() body: EnableTotpMfaDto, + ): Promise { + if (body.token) + return this.multiFactorAuthenticationService.enableMfa( + 'EMAIL', + userId, + body.token, + ); + await this.multiFactorAuthenticationService.requestEmailMfa(userId); + return { success: true }; + } +} diff --git a/src/modules/multi-factor-authentication/multi-factor-authentication.dto.ts b/src/modules/multi-factor-authentication/multi-factor-authentication.dto.ts new file mode 100644 index 00000000..c956bfb0 --- /dev/null +++ b/src/modules/multi-factor-authentication/multi-factor-authentication.dto.ts @@ -0,0 +1,17 @@ +import { IsOptional, IsPhoneNumber, IsString } from 'class-validator'; + +export class EnableTotpMfaDto { + @IsString() + @IsOptional() + token?: string; +} + +export class EnableSmsMfaDto { + @IsString() + @IsOptional() + token?: string; + + @IsPhoneNumber('ZZ') + @IsOptional() + phone?: string; +} diff --git a/src/modules/multi-factor-authentication/multi-factor-authentication.module.ts b/src/modules/multi-factor-authentication/multi-factor-authentication.module.ts new file mode 100644 index 00000000..9e97ed8b --- /dev/null +++ b/src/modules/multi-factor-authentication/multi-factor-authentication.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { AuthModule } from '../auth/auth.module'; +import { MailModule } from '../../providers/mail/mail.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { TwilioModule } from '../../providers/twilio/twilio.module'; +import { MultiFactorAuthenticationController } from './multi-factor-authentication.controller'; +import { MultiFactorAuthenticationService } from './multi-factor-authentication.service'; + +@Module({ + imports: [ + PrismaModule, + AuthModule, + TwilioModule, + MailModule, + ConfigModule, + TokensModule, + ], + controllers: [MultiFactorAuthenticationController], + providers: [MultiFactorAuthenticationService], +}) +export class MultiFactorAuthenticationModule {} diff --git a/src/modules/multi-factor-authentication/multi-factor-authentication.service.ts b/src/modules/multi-factor-authentication/multi-factor-authentication.service.ts new file mode 100644 index 00000000..8910ff1b --- /dev/null +++ b/src/modules/multi-factor-authentication/multi-factor-authentication.service.ts @@ -0,0 +1,136 @@ +import { + BadRequestException, + ConflictException, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { MfaMethod } from '@prisma/client'; +import { User } from '@prisma/client'; +import { hash } from 'bcrypt'; +import { + MFA_ENABLED_CONFLICT, + MFA_NOT_ENABLED, + NO_EMAILS, + USER_NOT_FOUND, +} from '../../errors/errors.constants'; +import { MailService } from '../../providers/mail/mail.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { TokensService } from '../../providers/tokens/tokens.service'; +import { TwilioService } from '../../providers/twilio/twilio.service'; +import { AuthService } from '../auth/auth.service'; + +@Injectable() +export class MultiFactorAuthenticationService { + constructor( + private prisma: PrismaService, + private auth: AuthService, + private configService: ConfigService, + private twilioService: TwilioService, + private emailService: MailService, + private tokensService: TokensService, + ) {} + + async requestTotpMfa(userId: number): Promise { + const enabled = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { twoFactorMethod: true }, + }); + if (!enabled) throw new NotFoundException(USER_NOT_FOUND); + if (enabled.twoFactorMethod !== 'NONE') + throw new ConflictException(MFA_ENABLED_CONFLICT); + return this.auth.getTotpQrCode(userId); + } + + async requestSmsMfa(userId: number, phone: string): Promise { + const enabled = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { twoFactorMethod: true }, + }); + if (!enabled) throw new NotFoundException(USER_NOT_FOUND); + if (enabled.twoFactorMethod !== 'NONE') + throw new ConflictException(MFA_ENABLED_CONFLICT); + const secret = this.auth.authenticator.generateSecret(); + await this.prisma.user.update({ + where: { id: userId }, + data: { twoFactorSecret: secret, twoFactorPhone: phone }, + }); + return this.twilioService.send({ + to: phone, + body: `${this.auth.getOneTimePassword(secret)} is your ${ + this.configService.get('meta.appName') ?? '' + } verification code.`, + }); + } + + async requestEmailMfa(userId: number): Promise { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { + twoFactorMethod: true, + prefersEmail: true, + name: true, + id: true, + }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (user.twoFactorMethod !== 'NONE') + throw new ConflictException(MFA_ENABLED_CONFLICT); + const secret = this.auth.authenticator.generateSecret(); + await this.prisma.user.update({ + where: { id: userId }, + data: { twoFactorSecret: secret }, + }); + if (!user.prefersEmail) throw new BadRequestException(NO_EMAILS); + return this.emailService.send({ + to: `"${user.name}" <${user.prefersEmail.emailSafe}>`, + template: 'auth/enable-email-mfa', + data: { + name: user.name, + code: this.auth.getOneTimePassword(secret), + }, + }); + } + + async enableMfa( + method: MfaMethod, + userId: number, + token: string, + ): Promise { + await this.auth.enableMfaMethod(method, userId, token); + return this.regenerateBackupCodes(userId); + } + + async disableMfa(userId: number): Promise> { + const enabled = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { twoFactorMethod: true }, + }); + if (!enabled) throw new NotFoundException(USER_NOT_FOUND); + if (enabled.twoFactorMethod === 'NONE') + throw new BadRequestException(MFA_NOT_ENABLED); + const user = await this.prisma.user.update({ + where: { id: userId }, + data: { twoFactorMethod: 'NONE', twoFactorSecret: null }, + }); + return this.prisma.expose(user); + } + + async regenerateBackupCodes(id: number) { + await this.prisma.backupCode.deleteMany({ where: { user: { id } } }); + const codes: string[] = []; + for await (const _ of [...Array(10)]) { + const unsafeCode = await this.tokensService.generateRandomString(10); + codes.push(unsafeCode); + const code = await hash( + unsafeCode, + this.configService.get('security.saltRounds') ?? 10, + ); + await this.prisma.backupCode.create({ + data: { user: { connect: { id } }, code }, + }); + } + return codes; + } +} diff --git a/src/modules/sessions/sessions.controller.ts b/src/modules/sessions/sessions.controller.ts new file mode 100644 index 00000000..bb5b1041 --- /dev/null +++ b/src/modules/sessions/sessions.controller.ts @@ -0,0 +1,69 @@ +import { + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Query, + Req, +} from '@nestjs/common'; +import { Session } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { UserRequest } from '../auth/auth.interface'; +import { Scopes } from '../auth/scope.decorator'; +import { SessionsService } from './sessions.service'; + +@Controller('users/:userId/sessions') +export class SessionController { + constructor(private sessionsService: SessionsService) {} + + /** Get sessions for a user */ + @Get() + @Scopes('user-{userId}:read-session-*') + async getAll( + @Req() req: UserRequest, + @Param('userId', ParseIntPipe) userId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.sessionsService.getSessions( + userId, + { + skip, + take, + orderBy, + cursor, + where, + }, + req.user?.sessionId, + ); + } + + /** Get a session for a user */ + @Get(':id') + @Scopes('user-{userId}:read-session-{id}') + async get( + @Req() req: UserRequest, + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.sessionsService.getSession(userId, id, req.user?.sessionId); + } + + /** Delete a session for a user */ + @Delete(':id') + @Scopes('user-{userId}:delete-session-{id}') + async remove( + @Param('userId', ParseIntPipe) userId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.sessionsService.deleteSession(userId, id); + } +} diff --git a/src/modules/sessions/sessions.module.ts b/src/modules/sessions/sessions.module.ts new file mode 100644 index 00000000..735ccc37 --- /dev/null +++ b/src/modules/sessions/sessions.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { SessionController } from './sessions.controller'; +import { SessionsService } from './sessions.service'; + +@Module({ + imports: [PrismaModule], + controllers: [SessionController], + providers: [SessionsService], +}) +export class SessionsModule {} diff --git a/src/modules/sessions/sessions.service.ts b/src/modules/sessions/sessions.service.ts new file mode 100644 index 00000000..5da0e837 --- /dev/null +++ b/src/modules/sessions/sessions.service.ts @@ -0,0 +1,77 @@ +import { + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import type { Prisma } from '@prisma/client'; +import { Session } from '@prisma/client'; +import { + SESSION_NOT_FOUND, + UNAUTHORIZED_RESOURCE, +} from '../../errors/errors.constants'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; + +@Injectable() +export class SessionsService { + constructor(private prisma: PrismaService) {} + + async getSessions( + userId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.SessionWhereUniqueInput; + where?: Prisma.SessionWhereInput; + orderBy?: Prisma.SessionOrderByInput; + }, + sessionId?: number, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const sessions = await this.prisma.session.findMany({ + skip, + take, + cursor, + where: { ...where, user: { id: userId } }, + orderBy, + }); + return sessions + .map((user) => this.prisma.expose(user)) + .map((i) => ({ ...i, isCurrentSession: sessionId === i.id })); + } catch (error) { + return []; + } + } + + async getSession( + userId: number, + id: number, + sessionId?: number, + ): Promise> { + const session = await this.prisma.session.findUnique({ + where: { id }, + }); + if (!session) throw new NotFoundException(SESSION_NOT_FOUND); + if (session.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + if (!session) throw new NotFoundException(SESSION_NOT_FOUND); + return { + ...this.prisma.expose(session), + isCurrentSession: sessionId === session.id, + }; + } + + async deleteSession(userId: number, id: number): Promise> { + const testSession = await this.prisma.session.findUnique({ + where: { id }, + }); + if (!testSession) throw new NotFoundException(SESSION_NOT_FOUND); + if (testSession.userId !== userId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const session = await this.prisma.session.delete({ + where: { id }, + }); + return this.prisma.expose(session); + } +} diff --git a/src/modules/stripe/stripe-billing.controller.ts b/src/modules/stripe/stripe-billing.controller.ts new file mode 100644 index 00000000..ff532281 --- /dev/null +++ b/src/modules/stripe/stripe-billing.controller.ts @@ -0,0 +1,87 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Post, + Put, +} from '@nestjs/common'; +import Stripe from 'stripe'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { + CreateBillingDto, + ReplaceBillingDto, + UpdateBillingDto, +} from './stripe.dto'; +import { StripeService } from './stripe.service'; + +@Controller('groups/:groupId/billing') +export class StripeBillingController { + constructor(private stripeService: StripeService) {} + + /** Create a billing account for a group */ + @Post() + @AuditLog('create-billing') + @Scopes('group-{groupId}:write-billing') + async createBillingAccount( + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: CreateBillingDto, + ): Promise { + return this.stripeService.createCustomer(groupId, data); + } + + /** Read billing for a group */ + @Get() + @Scopes('group-{groupId}:read-billing') + async getBillingAccount( + @Param('groupId', ParseIntPipe) groupId: number, + ): Promise { + return this.stripeService.getCustomer(groupId); + } + + /** Update billing for a group */ + @Patch() + @AuditLog('update-billing') + @Scopes('group-{groupId}:write-billing') + async updateBillingAccount( + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: UpdateBillingDto, + ): Promise { + return this.stripeService.updateCustomer(groupId, data); + } + + /** Replace billing for a group */ + @Put() + @AuditLog('update-billing') + @Scopes('group-{groupId}:write-billing') + async replaceBillingAccount( + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: ReplaceBillingDto, + ): Promise { + return this.stripeService.updateCustomer(groupId, data); + } + + /** Delete billing for a group */ + @Delete() + @AuditLog('delete-billing') + @Scopes('group-{groupId}:delete-billing') + async deleteBillingAccount( + @Param('groupId', ParseIntPipe) groupId: number, + ): Promise { + return this.stripeService.deleteCustomer(groupId); + } + + /** Get the billing portal link for a group */ + @Get('link') + @AuditLog('billing-portal') + @Scopes('group-{groupId}:write-billing') + async getSession( + @Param('groupId', ParseIntPipe) groupId: number, + ): Promise> { + return this.stripeService.getBillingPortalLink(groupId); + } +} diff --git a/src/modules/stripe/stripe-invoices.controller.ts b/src/modules/stripe/stripe-invoices.controller.ts new file mode 100644 index 00000000..4a449389 --- /dev/null +++ b/src/modules/stripe/stripe-invoices.controller.ts @@ -0,0 +1,32 @@ +import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'; +import Stripe from 'stripe'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { Scopes } from '../auth/scope.decorator'; +import { StripeService } from './stripe.service'; + +@Controller('groups/:groupId/invoices') +export class StripeInvoicesController { + constructor(private stripeService: StripeService) {} + + /** Read invoices for a group */ + @Get() + @Scopes('group-{groupId}:read-invoice-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: { id: string }, + ): Promise { + return this.stripeService.getInvoices(groupId, { take, cursor }); + } + + /** Read an invoice for a group */ + @Get(':id') + @Scopes('group-{groupId}:read-invoice-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id') id: string, + ): Promise { + return this.stripeService.getInvoice(groupId, id); + } +} diff --git a/src/modules/stripe/stripe-sources.controller.ts b/src/modules/stripe/stripe-sources.controller.ts new file mode 100644 index 00000000..16e5b47a --- /dev/null +++ b/src/modules/stripe/stripe-sources.controller.ts @@ -0,0 +1,63 @@ +import { + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Post, + Query, +} from '@nestjs/common'; +import Stripe from 'stripe'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { StripeService } from './stripe.service'; + +@Controller('groups/:groupId/sources') +export class StripeSourcesController { + constructor(private stripeService: StripeService) {} + + /** Create a source for a group */ + @Post() + @AuditLog('write-source') + @Scopes('group-{groupId}:write-source-*') + async create( + @Param('groupId', ParseIntPipe) groupId: number, + ): Promise { + return this.stripeService.createSession(groupId, 'setup'); + } + + /** Read sources for a group */ + @Get() + @Scopes('group-{groupId}:read-source-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: { id: string }, + ): Promise { + return this.stripeService.getSources(groupId, { take, cursor }); + } + + /** Read a source for a group */ + @Get(':id') + @Scopes('group-{groupId}:read-source-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id') id: string, + ): Promise { + return this.stripeService.getSource(groupId, id); + } + + /** Delete a source for a group */ + @Delete(':id') + @AuditLog('delete-source') + @Scopes('group-{groupId}:delete-source-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id') id: string, + ): Promise<{ success: true }> { + await this.stripeService.deleteSource(groupId, id); + return { success: true }; + } +} diff --git a/src/modules/stripe/stripe-subscription.controller.ts b/src/modules/stripe/stripe-subscription.controller.ts new file mode 100644 index 00000000..1cd8facd --- /dev/null +++ b/src/modules/stripe/stripe-subscription.controller.ts @@ -0,0 +1,73 @@ +import { + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Post, + Query, +} from '@nestjs/common'; +import Stripe from 'stripe'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { StripeService } from './stripe.service'; + +@Controller('groups/:groupId/subscriptions') +export class StripeSubscriptionController { + constructor(private stripeService: StripeService) {} + + /** Create a subscription for a group */ + @Post(':plan') + @AuditLog('create-subscription') + @Scopes('group-{groupId}:write-subscription-*') + async create( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('plan') plan: string, + ): Promise { + return this.stripeService.createSession(groupId, 'subscription', plan); + } + + /** Get subscriptions for a group */ + @Get() + @Scopes('group-{groupId}:read-subscription-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: { id: string }, + ): Promise { + return this.stripeService.getSubscriptions(groupId, { take, cursor }); + } + + /** Get a subscription for a group */ + @Get(':id') + @Scopes('group-{groupId}:read-subscription-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id') id: string, + ): Promise { + return this.stripeService.getSubscription(groupId, id); + } + + /** Cancel a subscription for a group */ + @Delete(':id') + @AuditLog('delete-subscription') + @Scopes('group-{groupId}:delete-subscription-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id') id: string, + ): Promise { + return this.stripeService.cancelSubscription(groupId, id); + } + + /** Get subscription plans for a group */ + @Get('plans') + @Scopes('group-{groupId}:write-subscription-*') + async getPlans( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('product') product?: string, + ): Promise { + return this.stripeService.plans(groupId, product); + } +} diff --git a/src/modules/stripe/stripe-webhook.controller.ts b/src/modules/stripe/stripe-webhook.controller.ts new file mode 100644 index 00000000..78b54bce --- /dev/null +++ b/src/modules/stripe/stripe-webhook.controller.ts @@ -0,0 +1,18 @@ +import { Body, Controller, Headers, Post } from '@nestjs/common'; +import { Public } from '../auth/public.decorator'; +import { StripeService } from './stripe.service'; + +@Controller('webhooks/stripe') +@Public() +export class StripeWebhookController { + constructor(private stripeService: StripeService) {} + + /** Handle a Stripe webhook */ + @Post() + async handleWebhook( + @Headers('stripe-signature') signature: string, + @Body() raw: Buffer, + ): Promise<{ received: true }> { + return this.stripeService.handleWebhook(signature, raw); + } +} diff --git a/src/modules/stripe/stripe.dto.ts b/src/modules/stripe/stripe.dto.ts new file mode 100644 index 00000000..c875d587 --- /dev/null +++ b/src/modules/stripe/stripe.dto.ts @@ -0,0 +1,104 @@ +import { + IsEmail, + IsNotEmpty, + IsObject, + IsOptional, + IsString, + Length, + ValidateNested, +} from 'class-validator'; + +class Address { + @IsString() + @IsNotEmpty() + line1!: string; + + @IsString() + @IsOptional() + city?: string; + + @IsString() + @IsOptional() + @Length(2) + country?: string; + + @IsString() + @IsOptional() + line2?: string; + + @IsString() + @IsOptional() + postal_code?: string; + + @IsString() + @IsOptional() + state?: string; +} + +export class CreateBillingDto { + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsString() + @IsNotEmpty() + name!: string; + + @IsString() + @IsOptional() + phone?: string; + + @IsString() + @IsOptional() + promotion_code?: string; + + @IsObject() + @ValidateNested() + @IsOptional() + address?: Address; +} + +export class UpdateBillingDto { + @IsString() + @IsOptional() + default_source?: string; + + @IsEmail() + @IsOptional() + email?: string; + + @IsString() + @IsOptional() + name?: string; + + @IsString() + @IsOptional() + phone?: string; + + @IsString() + @IsOptional() + promotion_code?: string; + + @IsObject() + @ValidateNested() + @IsOptional() + address?: Address; +} + +export class ReplaceBillingDto { + @IsEmail() + @IsNotEmpty() + email!: string; + + @IsString() + @IsNotEmpty() + name!: string; + + @IsString() + @IsNotEmpty() + phone!: string; + + @IsObject() + @ValidateNested() + address!: Address; +} diff --git a/src/modules/stripe/stripe.module.ts b/src/modules/stripe/stripe.module.ts new file mode 100644 index 00000000..0f8c6ff3 --- /dev/null +++ b/src/modules/stripe/stripe.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { StripeBillingController } from './stripe-billing.controller'; +import { StripeWebhookController } from './stripe-webhook.controller'; +import { StripeSubscriptionController } from './stripe-subscription.controller'; +import { StripeSourcesController } from './stripe-sources.controller'; +import { StripeInvoicesController } from './stripe-invoices.controller'; +import { StripeService } from './stripe.service'; + +@Module({ + imports: [ConfigModule, PrismaModule], + providers: [StripeService], + exports: [StripeService], + controllers: [ + StripeBillingController, + StripeInvoicesController, + StripeSourcesController, + StripeSubscriptionController, + StripeWebhookController, + ], +}) +export class StripeModule {} diff --git a/src/modules/stripe/stripe.service.ts b/src/modules/stripe/stripe.service.ts new file mode 100644 index 00000000..c131e2f8 --- /dev/null +++ b/src/modules/stripe/stripe.service.ts @@ -0,0 +1,267 @@ +import { + BadRequestException, + ConflictException, + Injectable, + Logger, + NotFoundException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import Stripe from 'stripe'; +import { + BILLING_ACCOUNT_CREATED_CONFLICT, + BILLING_NOT_FOUND, + CUSTOMER_NOT_FOUND, + GROUP_NOT_FOUND, + INVOICE_NOT_FOUND, + SOURCE_NOT_FOUND, + SUBSCRIPTION_NOT_FOUND, +} from '../../errors/errors.constants'; +import { PrismaService } from '../../providers/prisma/prisma.service'; + +@Injectable() +export class StripeService { + stripe: Stripe; + logger = new Logger(StripeService.name); + + constructor( + private configService: ConfigService, + private prisma: PrismaService, + ) { + const stripeApiKey = this.configService.get( + 'payments.stripeApiKey', + ); + this.stripe = new Stripe(stripeApiKey, { + apiVersion: '2020-08-27', + }); + } + + async createCustomer(groupId: number, data: Stripe.CustomerCreateParams) { + const group = await this.prisma.group.findUnique({ + where: { id: groupId }, + select: { attributes: true }, + }); + if (!group) throw new NotFoundException(GROUP_NOT_FOUND); + const attributes = group.attributes as { stripeCustomerId?: string }; + if (attributes?.stripeCustomerId) + throw new ConflictException(BILLING_ACCOUNT_CREATED_CONFLICT); + const result = await this.stripe.customers.create(data); + await this.prisma.group.update({ + where: { id: groupId }, + data: { attributes: { stripeCustomerId: result.id } }, + }); + return result as Stripe.Response; + } + + async getCustomer(groupId: number) { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.customers.retrieve(stripeId); + if (result.deleted) throw new NotFoundException(CUSTOMER_NOT_FOUND); + return result as Stripe.Response; + } + + async updateCustomer(groupId: number, data: Stripe.CustomerUpdateParams) { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.customers.update(stripeId, data); + return result as Stripe.Response; + } + + async deleteCustomer(groupId: number): Promise { + const stripeId = await this.stripeId(groupId); + const result = (await this.stripe.customers.del( + stripeId, + )) as Stripe.DeletedCustomer; + await this.prisma.group.update({ + where: { id: groupId }, + data: { attributes: { stripeCustomerId: null } }, + }); + return result; + } + + async getBillingPortalLink( + groupId: number, + ): Promise> { + const stripeId = await this.stripeId(groupId); + return this.stripe.billingPortal.sessions.create({ + customer: stripeId, + return_url: `${this.configService.get( + 'frontendUrl', + )}/groups/${groupId}`, + }); + } + + async getInvoices( + groupId: number, + params: { + take?: number; + cursor?: { id: string }; + }, + ): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.invoices.list({ + customer: stripeId, + limit: params.take, + starting_after: params.cursor?.id, + }); + return this.list(result); + } + + async getInvoice( + groupId: number, + invoiceId: string, + ): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.invoices.retrieve(invoiceId); + if (result.customer !== stripeId) + throw new NotFoundException(INVOICE_NOT_FOUND); + return result; + } + + async getSubscriptions( + groupId: number, + params: { + take?: number; + cursor?: { id: string }; + }, + ): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.subscriptions.list({ + customer: stripeId, + limit: params.take, + starting_after: params.cursor?.id, + }); + return this.list(result); + } + + async getSubscription( + groupId: number, + subscriptionId: string, + ): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.subscriptions.retrieve(subscriptionId); + if (result.customer !== stripeId) + throw new NotFoundException(SUBSCRIPTION_NOT_FOUND); + return result; + } + + async getSources( + groupId: number, + params: { + take?: number; + cursor?: { id: string }; + }, + ): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.customers.listSources(stripeId, { + limit: params.take, + starting_after: params.cursor?.id, + }); + return this.list(result); + } + + async getSource(groupId: number, sourceId: string): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.sources.retrieve(sourceId); + if (result.customer !== stripeId) + throw new NotFoundException(SOURCE_NOT_FOUND); + return result; + } + + async deleteSource(groupId: number, sourceId: string): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.sources.retrieve(sourceId); + if (result.customer !== stripeId) + throw new NotFoundException(SOURCE_NOT_FOUND); + await this.stripe.customers.deleteSource(stripeId, sourceId); + } + + async createSession( + groupId: number, + mode: Stripe.Checkout.SessionCreateParams.Mode, + planId?: string, + ): Promise { + const stripeId = await this.stripeId(groupId); + const data: Stripe.Checkout.SessionCreateParams = { + customer: stripeId, + mode, + payment_method_types: this.configService.get< + Array + >('payments.paymentMethodTypes') ?? ['card'], + success_url: `${this.configService.get( + 'frontendUrl', + )}/groups/${groupId}`, + cancel_url: `${this.configService.get( + 'frontendUrl', + )}/groups/${groupId}`, + }; + if (mode === 'subscription') + data.line_items = [{ quantity: 1, price: planId }]; + const result = await this.stripe.checkout.sessions.create(data); + return result; + } + + async cancelSubscription( + groupId: number, + subscriptionId: string, + ): Promise { + const stripeId = await this.stripeId(groupId); + const result = await this.stripe.subscriptions.retrieve(subscriptionId); + if (result.customer !== stripeId) + throw new NotFoundException(SUBSCRIPTION_NOT_FOUND); + return this.stripe.subscriptions.update(subscriptionId, { + cancel_at_period_end: true, + }); + } + + async plans(groupId: number, product?: string): Promise { + const stripeId = await this.stripeId(groupId); + const plans = await this.stripe.plans.list({ product }); + return plans.data.filter((plan) => { + let show = true; + ['special', 'internal'].forEach((word) => { + if (plan.nickname.toLowerCase().includes(word)) show = false; + }); + const tokens = plan.nickname + .toLowerCase() + .replace(/[^a-zA-Z0-9]/g, ' ') + .replace(/\s\s+/g, ' ') + .split(' '); + [stripeId, groupId.toString()].forEach((word) => { + if (tokens.includes(word)) show = true; + }); + return show; + }); + } + + async handleWebhook( + signature: string, + payload: Buffer, + ): Promise<{ received: true }> { + const event = this.stripe.webhooks.constructEvent( + payload, + signature, + this.configService.get('payments.stripeEndpointSecret') ?? '', + ); + switch (event.type) { + default: + this.logger.warn(`Unhandled event type ${event.type}`); + } + return { received: true }; + } + + private list(result: Stripe.Response>) { + return result.data; + } + + /** Get the Stripe customer ID from a group or throw an error */ + private async stripeId(groupId: number): Promise { + const group = await this.prisma.group.findUnique({ + where: { id: groupId }, + select: { attributes: true }, + }); + if (!group) throw new NotFoundException(GROUP_NOT_FOUND); + const attributes = group.attributes as { stripeCustomerId?: string }; + if (!attributes?.stripeCustomerId) + throw new BadRequestException(BILLING_NOT_FOUND); + return attributes.stripeCustomerId; + } +} diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts new file mode 100644 index 00000000..2a5545e4 --- /dev/null +++ b/src/modules/users/users.controller.ts @@ -0,0 +1,101 @@ +import { + BadRequestException, + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Post, + Query, + Req, + UploadedFiles, + UseInterceptors, +} from '@nestjs/common'; +import { FilesInterceptor } from '@nestjs/platform-express'; +import { User } from '@prisma/client'; +import { Files } from '../../helpers/interfaces'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { UserRequest } from '../auth/auth.interface'; +import { RateLimit } from '../auth/rate-limit.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { UpdateUserDto } from './users.dto'; +import { UsersService } from './users.service'; + +@Controller('users') +export class UserController { + constructor(private usersService: UsersService) {} + + /** Get users */ + @Get() + @Scopes('user-*:read-info') + async getAll( + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.usersService.getUsers({ skip, take, orderBy, cursor, where }); + } + + /** Get a user */ + @Get(':userId') + @Scopes('user-{userId}:read-info') + async get(@Param('userId', ParseIntPipe) id: number): Promise> { + return this.usersService.getUser(id); + } + + /** Update a user */ + @Patch(':userId') + @Scopes('user-{userId}:write-info') + async update( + @Req() request: UserRequest, + @Param('userId', ParseIntPipe) id: number, + @Body() data: UpdateUserDto, + ): Promise> { + return this.usersService.updateUser(id, data, request.user.role); + } + + /** Delete a user */ + @Delete(':userId') + @Scopes('user-{userId}:deactivate') + async remove( + @Param('userId', ParseIntPipe) id: number, + @Req() request: UserRequest, + ): Promise> { + return this.usersService.deactivateUser( + id, + request.user.type === 'user' && request.user?.id, + ); + } + + /** Upload profile picture */ + @Post(':userId/profile-picture') + @Scopes('user-{userId}:write-info') + @UseInterceptors(FilesInterceptor('files')) + async profilePicture( + @Param('userId', ParseIntPipe) id: number, + @UploadedFiles() files: Files, + ) { + if (files.length && files[0]) + return this.usersService.uploadProfilePicture(id, files[0]); + else throw new BadRequestException(); + } + + /** Send a link to merge two users */ + @Post(':userId/merge-request') + @Scopes('user-{userId}:merge') + @RateLimit(10) + async mergeRequest( + @Param('userId', ParseIntPipe) id: number, + @Body('email') email: string, + ): Promise<{ queued: true }> { + return this.usersService.requestMerge(id, email); + } +} diff --git a/src/modules/users/users.dto.ts b/src/modules/users/users.dto.ts new file mode 100644 index 00000000..1af9cac9 --- /dev/null +++ b/src/modules/users/users.dto.ts @@ -0,0 +1,75 @@ +import { MfaMethod } from '@prisma/client'; +import { + IsBoolean, + IsEnum, + IsIn, + IsLocale, + IsOptional, + IsString, + IsUrl, + Length, + MinLength, +} from 'class-validator'; + +export class UpdateUserDto { + @IsBoolean() + @IsOptional() + checkLocationOnLogin?: boolean; + + @IsString() + @Length(2, 2) + @IsOptional() + countryCode?: string; + + @IsString() + @IsIn(['MALE', 'FEMALE', 'NONBINARY', 'UNKNOWN']) + @IsOptional() + gender?: 'MALE' | 'FEMALE' | 'NONBINARY' | 'UNKNOWN'; + + @IsString() + @MinLength(3) + @IsOptional() + name?: string; + + @IsIn(['ACCOUNT', 'UPDATES', 'PROMOTIONS']) + @IsOptional() + notificationEmails?: 'ACCOUNT' | 'UPDATES' | 'PROMOTIONS'; + + @IsString() + @IsOptional() + newPassword?: string; + + @IsString() + @IsOptional() + currentPassword?: string; + + @IsBoolean() + @IsOptional() + ignorePwnedPassword?: boolean; + + @IsLocale() + @IsOptional() + prefersLanguage?: string; + + @IsString() + @IsIn(['NO_PREFERENCE', 'LIGHT', 'DARK']) + @IsOptional() + prefersColorScheme?: 'NO_PREFERENCE' | 'LIGHT' | 'DARK'; + + @IsString() + @IsIn(['NO_PREFERENCE', 'REDUCE']) + @IsOptional() + prefersReducedMotion?: 'NO_PREFERENCE' | 'REDUCE'; + + @IsUrl() + @IsOptional() + profilePictureUrl?: string; + + @IsString() + @IsOptional() + timezone?: string; + + @IsEnum(['NONE', 'TOTP', 'EMAIL']) + @IsOptional() + twoFactorMethod?: MfaMethod; +} diff --git a/src/modules/users/users.interface.ts b/src/modules/users/users.interface.ts new file mode 100644 index 00000000..58d45af4 --- /dev/null +++ b/src/modules/users/users.interface.ts @@ -0,0 +1,5 @@ +export interface PasswordUpdateInput { + currentPassword?: string; + newPassword?: string; + ignorePwnedPassword?: boolean; +} diff --git a/src/modules/users/users.module.ts b/src/modules/users/users.module.ts new file mode 100644 index 00000000..45f88ed2 --- /dev/null +++ b/src/modules/users/users.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { AuthModule } from '../auth/auth.module'; +import { MailModule } from '../../providers/mail/mail.module'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { UserController } from './users.controller'; +import { UsersService } from './users.service'; +import { S3Module } from '../../providers/s3/s3.module'; +import { ApiKeysModule } from '../api-keys/api-keys.module'; + +@Module({ + imports: [ + PrismaModule, + AuthModule, + MailModule, + ConfigModule, + TokensModule, + S3Module, + ApiKeysModule, + ], + controllers: [UserController], + providers: [UsersService], + exports: [UsersService], +}) +export class UsersModule {} diff --git a/src/modules/users/users.service.ts b/src/modules/users/users.service.ts new file mode 100644 index 00000000..8dae0547 --- /dev/null +++ b/src/modules/users/users.service.ts @@ -0,0 +1,215 @@ +import { + BadRequestException, + Injectable, + InternalServerErrorException, + NotFoundException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { Prisma, UserRole } from '@prisma/client'; +import { User } from '@prisma/client'; +import { compare } from 'bcrypt'; +import { extname } from 'path'; +import { + CURRENT_PASSWORD_REQUIRED, + FILE_TOO_LARGE, + INVALID_CREDENTIALS, + USER_NOT_FOUND, +} from '../../errors/errors.constants'; +import { Files } from '../../helpers/interfaces'; +import { safeEmail } from '../../helpers/safe-email'; +import { MailService } from '../../providers/mail/mail.service'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; +import { S3Service } from '../../providers/s3/s3.service'; +import { MERGE_ACCOUNTS_TOKEN } from '../../providers/tokens/tokens.constants'; +import { TokensService } from '../../providers/tokens/tokens.service'; +import { ApiKeysService } from '../api-keys/api-keys.service'; +import { AuthService } from '../auth/auth.service'; +import { PasswordUpdateInput } from './users.interface'; + +@Injectable() +export class UsersService { + constructor( + private prisma: PrismaService, + private auth: AuthService, + private email: MailService, + private configService: ConfigService, + private tokensService: TokensService, + private s3Service: S3Service, + private apiKeysService: ApiKeysService, + ) {} + + async getUser(id: number): Promise> { + const user = await this.prisma.user.findUnique({ + where: { id }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + return this.prisma.expose(user); + } + + async getUsers(params: { + skip?: number; + take?: number; + cursor?: Prisma.UserWhereUniqueInput; + where?: Prisma.UserWhereInput; + orderBy?: Prisma.UserOrderByInput; + }): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const users = await this.prisma.user.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + return users.map((user) => this.prisma.expose(user)); + } catch (error) { + return []; + } + } + + async updateUser( + id: number, + data: Omit & PasswordUpdateInput, + role?: UserRole, + ): Promise> { + const testUser = await this.prisma.user.findUnique({ where: { id } }); + if (!testUser) throw new NotFoundException(USER_NOT_FOUND); + const transformed: Prisma.UserUpdateInput & PasswordUpdateInput = data; + // If the user is updating their password + if (data.newPassword) { + if (!data.currentPassword) + throw new BadRequestException(CURRENT_PASSWORD_REQUIRED); + const user = await this.prisma.user.findUnique({ + where: { id }, + include: { prefersEmail: true }, + }); + const previousPassword = user?.password; + if (previousPassword) + if (!(await compare(data.currentPassword, previousPassword))) + throw new BadRequestException(INVALID_CREDENTIALS); + transformed.password = await this.auth.hashAndValidatePassword( + data.newPassword, + !!data.ignorePwnedPassword, + ); + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'users/password-changed', + data: { + name: user.name, + }, + }); + } + delete transformed.currentPassword; + delete transformed.newPassword; + delete transformed.ignorePwnedPassword; + if (role !== 'SUDO') delete transformed.role; + const updateData: Prisma.UserUpdateInput = transformed; + const user = await this.prisma.user.update({ + data: updateData, + where: { id }, + }); + // If the role of this user has changed + if (transformed.role && testUser.role !== transformed.role) { + // Log out from all sessions since their scopes have changed + await this.prisma.session.deleteMany({ where: { user: { id } } }); + // Remove all scopes now allowed anymore from API keys + await this.apiKeysService.cleanAllApiKeysForUser(id); + } + return this.prisma.expose(user); + } + + async deactivateUser( + id: number, + deactivatedBy?: number, + ): Promise> { + const user = await this.prisma.user.update({ + where: { id }, + data: { active: false }, + include: { prefersEmail: true }, + }); + await this.prisma.session.deleteMany({ where: { user: { id } } }); + if (deactivatedBy === id) + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'users/deactivated', + data: { + name: user.name, + }, + }); + return this.prisma.expose(user); + } + + async deleteUser(id: number): Promise> { + const testUser = await this.prisma.user.findUnique({ where: { id } }); + if (!testUser) throw new NotFoundException(USER_NOT_FOUND); + await this.prisma.membership.deleteMany({ where: { user: { id } } }); + await this.prisma.email.deleteMany({ where: { user: { id } } }); + await this.prisma.session.deleteMany({ where: { user: { id } } }); + await this.prisma.approvedSubnet.deleteMany({ where: { user: { id } } }); + await this.prisma.backupCode.deleteMany({ where: { user: { id } } }); + await this.prisma.identity.deleteMany({ where: { user: { id } } }); + await this.prisma.auditLog.deleteMany({ where: { user: { id } } }); + await this.prisma.apiKey.deleteMany({ where: { user: { id } } }); + const user = await this.prisma.user.delete({ where: { id } }); + return this.prisma.expose(user); + } + + async requestMerge(userId: number, email: string): Promise<{ queued: true }> { + const emailSafe = safeEmail(email); + const user = await this.prisma.user.findFirst({ + where: { emails: { some: { emailSafe } } }, + include: { prefersEmail: true }, + }); + if (!user) throw new NotFoundException(USER_NOT_FOUND); + if (user.id === userId) throw new NotFoundException(USER_NOT_FOUND); + const minutes = parseInt( + this.configService.get('security.mergeUsersTokenExpiry') ?? '', + ); + this.email.send({ + to: `"${user.name}" <${user.prefersEmail.email}>`, + template: 'users/merge-request', + data: { + name: user.name, + minutes, + link: `${this.configService.get( + 'frontendUrl', + )}/auth/link/merge-accounts?token=${this.tokensService.signJwt( + MERGE_ACCOUNTS_TOKEN, + { baseUserId: userId, mergeUserId: user.id }, + `${minutes}m`, + )}`, + }, + }); + return { queued: true }; + } + + async uploadProfilePicture( + id: number, + file: Files[0], + ): Promise> { + if (file.size > 25000000) throw new Error(FILE_TOO_LARGE); + if (!this.configService.get('s3.profilePictureBucket')) + throw new InternalServerErrorException('Profile picture bucket not set'); + const { Location } = await this.s3Service.upload( + `picture-${id}-${this.tokensService.generateUuid()}${extname( + file.originalname, + )}`, + file.buffer, + this.configService.get('s3.profilePictureBucket'), + true, + ); + return this.prisma.user.update({ + where: { id }, + data: { + profilePictureUrl: Location.replace( + `${this.configService.get( + 's3.profilePictureBucket', + )}.s3.${this.configService.get('s3.region')}.amazonaws.com`, + this.configService.get('s3.profilePictureCdnHostname'), + ), + }, + }); + } +} diff --git a/src/modules/webhooks/webhooks.controller.ts b/src/modules/webhooks/webhooks.controller.ts new file mode 100644 index 00000000..c5aee218 --- /dev/null +++ b/src/modules/webhooks/webhooks.controller.ts @@ -0,0 +1,114 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Post, + Put, + Query, +} from '@nestjs/common'; +import { Webhook } from '@prisma/client'; +import { CursorPipe } from '../../pipes/cursor.pipe'; +import { OptionalIntPipe } from '../../pipes/optional-int.pipe'; +import { OrderByPipe } from '../../pipes/order-by.pipe'; +import { WherePipe } from '../../pipes/where.pipe'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { AuditLog } from '../audit-logs/audit-log.decorator'; +import { Scopes } from '../auth/scope.decorator'; +import { + CreateWebhookDto, + ReplaceWebhookDto, + UpdateWebhookDto, +} from './webhooks.dto'; +import { WebhooksService } from './webhooks.service'; + +@Controller('groups/:groupId/webhooks') +export class WebhookController { + constructor(private webhooksService: WebhooksService) {} + + /** Create a webhook for a group */ + @Post() + @AuditLog('create-webhook') + @Scopes('group-{groupId}:write-webhook-*') + async create( + @Param('groupId', ParseIntPipe) groupId: number, + @Body() data: CreateWebhookDto, + ): Promise> { + return this.webhooksService.createWebhook(groupId, data); + } + + /** Get webhooks for a group */ + @Get() + @Scopes('group-{groupId}:read-webhook-*') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.webhooksService.getWebhooks(groupId, { + skip, + take, + orderBy, + cursor, + where, + }); + } + + /** Get webhook scopes for a group */ + @Get('scopes') + @Scopes('group-{groupId}:write-webhook-*') + async scopes(): Promise> { + return this.webhooksService.getWebhookScopes(); + } + + /** Get a webhook for a group */ + @Get(':id') + @Scopes('group-{groupId}:read-webhook-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.webhooksService.getWebhook(groupId, id); + } + + /** Update a webhook for a group */ + @Patch(':id') + @AuditLog('update-webhook') + @Scopes('group-{groupId}:write-webhook-{id}') + async update( + @Body() data: UpdateWebhookDto, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.webhooksService.updateWebhook(groupId, id, data); + } + + /** Replace a webhook for a group */ + @Put(':id') + @AuditLog('update-webhook') + @Scopes('group-{groupId}:write-webhook-{id}') + async replace( + @Body() data: ReplaceWebhookDto, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.webhooksService.updateWebhook(groupId, id, data); + } + + /** Delete a webhook for a group */ + @Delete(':id') + @AuditLog('delete-webhook') + @Scopes('group-{groupId}:delete-webhook-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.webhooksService.deleteWebhook(groupId, id); + } +} diff --git a/src/modules/webhooks/webhooks.dto.ts b/src/modules/webhooks/webhooks.dto.ts new file mode 100644 index 00000000..65cb7561 --- /dev/null +++ b/src/modules/webhooks/webhooks.dto.ts @@ -0,0 +1,67 @@ +import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +export class CreateWebhookDto { + @IsString() + @IsNotEmpty() + url: string; + + @IsString() + @IsNotEmpty() + event: string; + + @IsString() + @IsOptional() + contentType?: string; + + @IsBoolean() + @IsOptional() + isActive?: boolean; + + @IsString() + @IsOptional() + secret?: string; +} + +export class UpdateWebhookDto { + @IsString() + @IsOptional() + url?: string; + + @IsString() + @IsOptional() + event?: string; + + @IsString() + @IsOptional() + contentType?: string; + + @IsBoolean() + @IsOptional() + isActive?: boolean; + + @IsString() + @IsOptional() + secret?: string; +} + +export class ReplaceWebhookDto { + @IsString() + @IsNotEmpty() + url!: string; + + @IsString() + @IsNotEmpty() + event!: string; + + @IsString() + @IsOptional() + contentType!: string; + + @IsBoolean() + @IsOptional() + isActive!: boolean; + + @IsString() + @IsOptional() + secret!: string; +} diff --git a/src/modules/webhooks/webhooks.module.ts b/src/modules/webhooks/webhooks.module.ts new file mode 100644 index 00000000..91f4f445 --- /dev/null +++ b/src/modules/webhooks/webhooks.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../providers/prisma/prisma.module'; +import { StripeModule } from '../stripe/stripe.module'; +import { TokensModule } from '../../providers/tokens/tokens.module'; +import { WebhookController } from './webhooks.controller'; +import { WebhooksService } from './webhooks.service'; +import { ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [PrismaModule, TokensModule, StripeModule, ConfigModule], + controllers: [WebhookController], + providers: [WebhooksService], + exports: [WebhooksService], +}) +export class WebhooksModule {} diff --git a/src/modules/webhooks/webhooks.service.ts b/src/modules/webhooks/webhooks.service.ts new file mode 100644 index 00000000..b5312752 --- /dev/null +++ b/src/modules/webhooks/webhooks.service.ts @@ -0,0 +1,195 @@ +import { + Injectable, + Logger, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import type { Prisma } from '@prisma/client'; +import { Webhook } from '@prisma/client'; +import got from 'got'; +import PQueue from 'p-queue'; +import pRetry from 'p-retry'; +import { + UNAUTHORIZED_RESOURCE, + WEBHOOK_NOT_FOUND, +} from '../../errors/errors.constants'; +import { Expose } from '../../providers/prisma/prisma.interface'; +import { PrismaService } from '../../providers/prisma/prisma.service'; + +@Injectable() +export class WebhooksService { + private readonly logger = new Logger(WebhooksService.name); + private queue = new PQueue({ concurrency: 1 }); + + constructor( + private prisma: PrismaService, + private configService: ConfigService, + ) {} + + async createWebhook( + groupId: number, + data: Omit, 'group'>, + ): Promise { + return this.prisma.webhook.create({ + data: { ...data, group: { connect: { id: groupId } } }, + }); + } + + async getWebhooks( + groupId: number, + params: { + skip?: number; + take?: number; + cursor?: Prisma.WebhookWhereUniqueInput; + where?: Prisma.WebhookWhereInput; + orderBy?: Prisma.WebhookOrderByInput; + }, + ): Promise[]> { + const { skip, take, cursor, where, orderBy } = params; + try { + const webhooks = await this.prisma.webhook.findMany({ + skip, + take, + cursor, + where: { ...where, group: { id: groupId } }, + orderBy, + }); + return webhooks.map((group) => this.prisma.expose(group)); + } catch (error) { + return []; + } + } + + async getWebhook(groupId: number, id: number): Promise> { + const webhook = await this.prisma.webhook.findUnique({ + where: { id }, + }); + if (!webhook) throw new NotFoundException(WEBHOOK_NOT_FOUND); + if (webhook.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + return this.prisma.expose(webhook); + } + + async updateWebhook( + groupId: number, + id: number, + data: Prisma.WebhookUpdateInput, + ): Promise> { + const testWebhook = await this.prisma.webhook.findUnique({ + where: { id }, + }); + if (!testWebhook) throw new NotFoundException(WEBHOOK_NOT_FOUND); + if (testWebhook.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const webhook = await this.prisma.webhook.update({ + where: { id }, + data, + }); + return this.prisma.expose(webhook); + } + + async replaceWebhook( + groupId: number, + id: number, + data: Prisma.WebhookCreateInput, + ): Promise> { + const testWebhook = await this.prisma.webhook.findUnique({ + where: { id }, + }); + if (!testWebhook) throw new NotFoundException(WEBHOOK_NOT_FOUND); + if (testWebhook.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const webhook = await this.prisma.webhook.update({ + where: { id }, + data, + }); + return this.prisma.expose(webhook); + } + + async deleteWebhook(groupId: number, id: number): Promise> { + const testWebhook = await this.prisma.webhook.findUnique({ + where: { id }, + }); + if (!testWebhook) throw new NotFoundException(WEBHOOK_NOT_FOUND); + if (testWebhook.groupId !== groupId) + throw new UnauthorizedException(UNAUTHORIZED_RESOURCE); + const webhook = await this.prisma.webhook.delete({ + where: { id }, + }); + return this.prisma.expose(webhook); + } + + async getWebhookScopes(): Promise> { + const scopes: Record = { + 'create-api-key': 'Create API key', + 'update-api-key': 'Update API key', + 'delete-api-key': 'Delete API key', + 'create-domain': 'Create domain', + 'delete-domain': 'Delete domain', + 'verify-domain-txt': 'Verify domain (TXT)', + 'verify-domain-html': 'Verify domain (HTML)', + 'update-info': 'Update info', + delete: 'Delete group', + 'add-membership': 'Add membership', + 'update-membership': 'Update membership', + 'delete-membership': 'Delete membership', + 'create-billing': 'Create billing', + 'update-billing': 'Update billing', + 'delete-billing': 'Delete billing', + 'write-source': 'Write source', + 'delete-source': 'Delete source', + 'create-subscription': 'Create subscription', + 'delete-subscription': 'Delete subscription', + 'create-webhook': 'Create webhook', + 'update-webhook': 'Update webhook', + 'delete-webhook': 'Delete webhook', + }; + return scopes; + } + + triggerWebhook(groupId: number, event: string) { + this.prisma.webhook + .findMany({ + where: { group: { id: groupId }, isActive: true, event }, + }) + .then((webhooks) => { + webhooks.forEach((webhook) => + this.queue + .add(() => + pRetry(() => this.callWebhook(webhook, event), { + retries: + this.configService.get('webhooks.retries') ?? 3, + onFailedAttempt: (error) => { + this.logger.error( + `Triggering webhoook failed, retrying (${error.retriesLeft} attempts left)`, + error.name, + ); + if (error.retriesLeft === 0) + this.prisma.webhook + .update({ + where: { id: webhook.id }, + data: { isActive: false }, + }) + .then(() => {}) + .catch(() => {}); + }, + }), + ) + .then(() => {}) + .catch(() => {}), + ); + }) + .catch((error) => this.logger.error('Unable to get webhooks', error)); + } + + private async callWebhook(webhook: Webhook, event: string) { + if (webhook.contentType === 'application/json') + await got(webhook.url, { method: 'POST', json: { event } }); + else await got(webhook.url, { method: 'POST', body: event }); + await this.prisma.webhook.update({ + where: { id: webhook.id }, + data: { lastFiredAt: new Date() }, + }); + } +} diff --git a/src/pipes/cursor-slug.pipe.ts b/src/pipes/cursor-slug.pipe.ts new file mode 100644 index 00000000..30c446e3 --- /dev/null +++ b/src/pipes/cursor-slug.pipe.ts @@ -0,0 +1,24 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { CURSOR_PIPE_FORMAT } from '../errors/errors.constants'; +import { parseObjectLiteral } from '../helpers/parse-object-literal'; + +/** Convert a string like "slug: "ok", name: 'Anand'" to { slug: "ok", name: "Anand" } */ +@Injectable() +export class CursorSlugPipe implements PipeTransform { + transform(value: string): Record | undefined { + if (value == null) return undefined; + if (!value.includes(':')) value = `slug:${value}`; + try { + const rules = parseObjectLiteral(value); + const items: Record = {}; + rules.forEach((rule) => { + const num = String(rule[1]); + if (!num) items[rule[0]] = num; + else if (rule[1]) items[rule[0]] = rule[1]; + }); + return items; + } catch (_) { + throw new BadRequestException(CURSOR_PIPE_FORMAT); + } + } +} diff --git a/src/pipes/cursor.pipe.ts b/src/pipes/cursor.pipe.ts new file mode 100644 index 00000000..abacfb11 --- /dev/null +++ b/src/pipes/cursor.pipe.ts @@ -0,0 +1,24 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { CURSOR_PIPE_FORMAT } from '../errors/errors.constants'; +import { parseObjectLiteral } from '../helpers/parse-object-literal'; + +/** Convert a string like "id: 12, b: 'Anand'" to { id: 12, name: "Anand" } */ +@Injectable() +export class CursorPipe implements PipeTransform { + transform(value: string): Record | undefined { + if (value == null) return undefined; + if (!value.includes(':')) value = `id:${value}`; + try { + const rules = parseObjectLiteral(value); + const items: Record = {}; + rules.forEach((rule) => { + const num = Number(rule[1]); + if (!isNaN(num)) items[rule[0]] = num; + else if (rule[1]) items[rule[0]] = rule[1]; + }); + return items; + } catch (_) { + throw new BadRequestException(CURSOR_PIPE_FORMAT); + } + } +} diff --git a/src/pipes/optional-int.pipe.ts b/src/pipes/optional-int.pipe.ts new file mode 100644 index 00000000..b38c0713 --- /dev/null +++ b/src/pipes/optional-int.pipe.ts @@ -0,0 +1,21 @@ +import { + ArgumentMetadata, + BadRequestException, + Injectable, + PipeTransform, +} from '@nestjs/common'; +import { OPTIONAL_INT_PIPE_NUMBER } from '../errors/errors.constants'; + +/** Convert a string like "1" to a number, but without NaN */ +@Injectable() +export class OptionalIntPipe implements PipeTransform { + transform(value: string, metadata: ArgumentMetadata): number | undefined { + if (value == null) return undefined; + const num = Number(value); + if (isNaN(num)) + throw new BadRequestException( + OPTIONAL_INT_PIPE_NUMBER.replace('$key', metadata.data), + ); + return num; + } +} diff --git a/src/pipes/order-by.pipe.ts b/src/pipes/order-by.pipe.ts new file mode 100644 index 00000000..461dbb7e --- /dev/null +++ b/src/pipes/order-by.pipe.ts @@ -0,0 +1,32 @@ +import { + ArgumentMetadata, + BadGatewayException, + BadRequestException, + Injectable, + PipeTransform, +} from '@nestjs/common'; +import { ORDER_BY_ASC_DESC, ORDER_BY_FORMAT } from '../errors/errors.constants'; + +/** Convert a string like "name asc, address desc" to { name: "asc", address: "desc" } */ +@Injectable() +export class OrderByPipe implements PipeTransform { + transform( + value: string, + metadata: ArgumentMetadata, + ): Record | undefined { + if (value == null) return undefined; + try { + const rules = value.split(',').map((val) => val.trim()); + const orderBy: Record = {}; + rules.forEach((rule) => { + const [key, order] = rule.split(':') as [string, 'asc' | 'desc']; + if (!['asc', 'desc'].includes(order.toLocaleLowerCase())) + throw new BadGatewayException(ORDER_BY_ASC_DESC); + orderBy[key] = order.toLocaleLowerCase() as 'asc' | 'desc'; + }); + return orderBy; + } catch (_) { + throw new BadRequestException(ORDER_BY_FORMAT); + } + } +} diff --git a/src/pipes/select-include.pipe.ts b/src/pipes/select-include.pipe.ts new file mode 100644 index 00000000..20a434fd --- /dev/null +++ b/src/pipes/select-include.pipe.ts @@ -0,0 +1,23 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { SELECT_INCLUDE_PIPE_FORMAT } from '../errors/errors.constants'; +import { object } from 'dot-object'; + +/** + * Convert a string like "id,createdAt,user.name,user.id" + * => { id: true, createdAt: true, user: { name: true, id: true } } + */ +@Injectable() +export class SelectIncludePipe implements PipeTransform { + transform(value: string): Record | undefined { + if (value == null) return undefined; + try { + const testRecord: Record = {}; + value.split(',').forEach((i) => { + if (/^[a-z0-9\.]+$/i.test(i.trim())) testRecord[i.trim()] = true; + }); + return object(testRecord) as Record; + } catch (_) { + throw new BadRequestException(SELECT_INCLUDE_PIPE_FORMAT); + } + } +} diff --git a/src/pipes/where.pipe.ts b/src/pipes/where.pipe.ts new file mode 100644 index 00000000..ad3e64f5 --- /dev/null +++ b/src/pipes/where.pipe.ts @@ -0,0 +1,71 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { WHERE_PIPE_FORMAT } from '../errors/errors.constants'; +import { parseObjectLiteral } from '../helpers/parse-object-literal'; + +/** Convert a string like "id: 12, b: 'Anand'" to { id: 12, name: "Anand" } */ +@Injectable() +export class WherePipe implements PipeTransform { + transform(value: string): Record | undefined { + if (value == null) return undefined; + try { + const rules = parseObjectLiteral(value); + const items: Record = {}; + rules.forEach((rule) => { + const ruleKey = rule[0]; + let ruleValue: any = rule[1]; + if (ruleValue.endsWith(')')) { + if (ruleValue.startsWith('int(')) + ruleValue = parseInt(/\(([^)]+)\)/.exec(ruleValue)[1]); + else if ( + ruleValue.startsWith('date(') || + ruleValue.startsWith('datetime(') + ) + ruleValue = new Date( + /\(([^)]+)\)/.exec(ruleValue)[1], + ).toISOString(); + else if (ruleValue.startsWith('float(')) + ruleValue = parseFloat(/\(([^)]+)\)/.exec(ruleValue)[1]); + else if (ruleValue.startsWith('string(')) + ruleValue = /\(([^)]+)\)/.exec(ruleValue)[1]; + else if ( + ruleValue.startsWith('boolean(') || + ruleValue.startsWith('bool(') + ) + ruleValue = /\(([^)]+)\)/.exec(ruleValue)[1] === 'true'; + } + [ + 'lt', + 'lte', + 'gt', + 'gte', + 'equals', + 'not', + 'contains', + 'startsWith', + 'endsWith', + 'every', + 'some', + 'none', + ].forEach((val) => { + if (rule[1].startsWith(`${val} `)) { + const data: Record = {}; + data[val] = ruleValue.replace(`${val} `, ''); + if (data[val].includes(':') && !data[val].endsWith(':')) { + const record: Record = {}; + record[data[val].split(':')[0].trim()] = data[val] + .split(':')[1] + .trim(); + data[val] = record; + } + items[ruleKey] = data; + } + }); + if (ruleValue != null && ruleValue !== '') + items[ruleKey] = items[ruleKey] || ruleValue; + }); + return items; + } catch (error) { + throw new BadRequestException(WHERE_PIPE_FORMAT); + } + } +} diff --git a/src/providers/cloudinary/cloudinary.module.ts b/src/providers/cloudinary/cloudinary.module.ts new file mode 100644 index 00000000..7d4153c6 --- /dev/null +++ b/src/providers/cloudinary/cloudinary.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { CloudinaryService } from './cloudinary.service'; + +@Module({ + imports: [ConfigModule], + providers: [CloudinaryService], + exports: [CloudinaryService], +}) +export class CloudinaryModule {} diff --git a/src/providers/cloudinary/cloudinary.service.ts b/src/providers/cloudinary/cloudinary.service.ts new file mode 100644 index 00000000..688d87a6 --- /dev/null +++ b/src/providers/cloudinary/cloudinary.service.ts @@ -0,0 +1,64 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import cloudinary, { UploadApiResponse } from 'cloudinary'; +import { Readable, Stream } from 'stream'; +import { Configuration } from '../../config/configuration.interface'; + +/** + * MultiStream class helps convert buffers to a streams + * @source https://github.com/gagle/node-streamifier + */ +class MultiStream extends Readable { + _object: Buffer | string | undefined; + constructor(object: Buffer | string, options: any = {}) { + super(); + this._object = object; + Stream.Readable.call(this, { + highWaterMark: options.highWaterMark, + encoding: options.encoding, + }); + } + _read() { + this.push(this._object); + this._object = undefined; + } +} + +/** + * + * @param object - Object to convert + * @param options - Configuration (encoding and highWaterMark) + */ +const createReadStream = (object: Buffer | string, options?: any) => + new MultiStream(object, options); + +@Injectable() +export class CloudinaryService { + private logger = new Logger(CloudinaryService.name); + + constructor(private configService: ConfigService) { + const config = this.configService.get( + 'cloudinary', + ); + if (config.cloudName) + cloudinary.v2.config({ + cloud_name: config.cloudName, + api_key: config.apiKey, + api_secret: config.apiSecret, + }); + else this.logger.warn('Cloudinary API key not found'); + } + + upload(buffer: Buffer | string, folder: string): Promise { + return new Promise((resolve, reject) => { + const uploadStream = cloudinary.v2.uploader.upload_stream( + { folder }, + (error, result) => { + if (result) return resolve(result); + reject(error); + }, + ); + createReadStream(buffer).pipe(uploadStream); + }); + } +} diff --git a/src/providers/dns/dns.interface.ts b/src/providers/dns/dns.interface.ts new file mode 100644 index 00000000..4e9cae6e --- /dev/null +++ b/src/providers/dns/dns.interface.ts @@ -0,0 +1,23 @@ +import { AnyRecord, MxRecord, NaptrRecord, SoaRecord, SrvRecord } from 'dns'; + +export type RecordType = + | 'A' + | 'AAAA' + | 'ANY' + | 'CNAME' + | 'MX' + | 'NAPTR' + | 'NS' + | 'PTR' + | 'SOA' + | 'SRV' + | 'TXT'; + +export type RecordResult = + | Array + | Array + | Array + | SoaRecord + | Array + | Array> + | Array; diff --git a/src/providers/dns/dns.module.ts b/src/providers/dns/dns.module.ts new file mode 100644 index 00000000..7b9f7294 --- /dev/null +++ b/src/providers/dns/dns.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { DnsService } from './dns.service'; + +@Module({ + providers: [DnsService], + exports: [DnsService], +}) +export class DnsModule {} diff --git a/src/providers/dns/dns.service.ts b/src/providers/dns/dns.service.ts new file mode 100644 index 00000000..d52f4227 --- /dev/null +++ b/src/providers/dns/dns.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@nestjs/common'; +import dns from 'dns'; +import { RecordResult, RecordType } from './dns.interface'; + +@Injectable() +export class DnsService { + async lookup( + hostname: string, + recordType: RecordType, + ): Promise { + try { + return await this.unsafeLookup(hostname, recordType); + } catch (error) { + return []; + } + } + + private unsafeLookup( + hostname: string, + recordType: RecordType, + ): Promise { + return new Promise((resolve, reject) => { + dns.resolve(hostname, recordType, (error, records) => { + if (error) return reject(error); + resolve(records); + }); + }); + } +} diff --git a/src/providers/elasticsearch/elasticsearch.module.ts b/src/providers/elasticsearch/elasticsearch.module.ts new file mode 100644 index 00000000..dd4079d5 --- /dev/null +++ b/src/providers/elasticsearch/elasticsearch.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ElasticSearchService } from './elasticsearch.service'; + +@Module({ + imports: [ConfigModule], + providers: [ElasticSearchService], + exports: [ElasticSearchService], +}) +export class ElasticSearchModule {} diff --git a/src/providers/elasticsearch/elasticsearch.service.ts b/src/providers/elasticsearch/elasticsearch.service.ts new file mode 100644 index 00000000..be8b812e --- /dev/null +++ b/src/providers/elasticsearch/elasticsearch.service.ts @@ -0,0 +1,102 @@ +import { Client } from '@elastic/elasticsearch'; +import { Index, Search } from '@elastic/elasticsearch/api/requestParams'; +import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import createAwsElasticsearchConnector from 'aws-elasticsearch-connector'; +import AWS from 'aws-sdk'; +import PQueue from 'p-queue'; +import pRetry from 'p-retry'; +import { Configuration } from '../../config/configuration.interface'; + +@Injectable() +export class ElasticSearchService { + private logger = new Logger(ElasticSearchService.name); + private queue = new PQueue({ concurrency: 1 }); + client?: Client; + + constructor(private configService: ConfigService) { + const config = this.configService.get( + 'elasticSearch', + ); + if (config.aws?.accessKeyId) { + AWS.config.update({ + accessKeyId: config.aws.accessKeyId, + secretAccessKey: config.aws.secretAccessKey, + region: config.aws.region, + }); + this.client = new Client({ + ...createAwsElasticsearchConnector(AWS.config), + node: config.node, + }); + } else if (config.node) + this.client = new Client({ + auth: config.auth, + node: config.node, + }); + else this.logger.warn('ElasticSearch tracking is not enabled'); + } + + index(index: string, record: Record, params?: Index) { + if (this.client) + this.queue + .add(() => + pRetry(() => this.indexRecord(index, record, params), { + retries: + this.configService.get('elasticSearch.retries') ?? 3, + onFailedAttempt: (error) => { + this.logger.error( + `Indexing record failed, retrying (${error.retriesLeft} attempts left)`, + error.name, + ); + }, + }), + ) + .then(() => {}) + .catch(() => {}); + } + + search( + params?: Search>, + options?: TransportRequestOptions, + ) { + if (this.client) return this.client.search(params, options); + } + + /** + * Delete old records from ElasticSearch + * @param index - Index + * @param days - Number of days ago (e.g., 30 will delete month-old data) + */ + deleteOldRecords = async (index: string, days: number) => { + const now = new Date(); + now.setDate(now.getDate() - days); + if (this.client) + return this.client.deleteByQuery({ + index, + body: { + query: { + bool: { + must: [ + { + range: { + date: { + lte: now, + }, + }, + }, + ], + }, + }, + }, + }); + }; + + private async indexRecord( + index: string, + record: Record, + params?: Index, + ) { + return this.client.index({ index, body: record, ...params }); + } +} diff --git a/src/providers/geolocation/geolocation.module.ts b/src/providers/geolocation/geolocation.module.ts new file mode 100644 index 00000000..fb715da2 --- /dev/null +++ b/src/providers/geolocation/geolocation.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { GeolocationService } from './geolocation.service'; + +@Module({ + imports: [ConfigModule], + providers: [GeolocationService], + exports: [GeolocationService], +}) +export class GeolocationModule {} diff --git a/src/providers/geolocation/geolocation.service.spec.ts b/src/providers/geolocation/geolocation.service.spec.ts new file mode 100644 index 00000000..a937818a --- /dev/null +++ b/src/providers/geolocation/geolocation.service.spec.ts @@ -0,0 +1,40 @@ +import { ConfigService } from '@nestjs/config'; +import { GeolocationService } from './geolocation.service'; + +const geolocationService = new GeolocationService(new ConfigService()); + +describe('GeolocationService', () => { + describe('getLocation', () => { + it('gets geolocation', async () => { + const geolocation = await geolocationService.getLocation( + '182.64.221.140', + ); + expect(geolocation).toBeDefined(); + }); + + it('gets country in result', async () => { + const geolocation = await geolocationService.getLocation( + '182.64.221.140', + ); + expect(geolocation.country).toBeDefined(); + }); + + it('gets correct country', async () => { + const geolocation = await geolocationService.getLocation( + '182.64.221.140', + ); + expect(geolocation.country.iso_code).toBe('IN'); + }); + + it('gets correct timezone', async () => { + const geolocation = await geolocationService.getLocation( + '182.64.221.140', + ); + expect(geolocation.location.time_zone).toBe('Asia/Kolkata'); + }); + }); + + afterAll(() => { + geolocationService.onModuleDestroy(); + }); +}); diff --git a/src/providers/geolocation/geolocation.service.ts b/src/providers/geolocation/geolocation.service.ts new file mode 100644 index 00000000..3cbfbc99 --- /dev/null +++ b/src/providers/geolocation/geolocation.service.ts @@ -0,0 +1,48 @@ +import { Injectable, OnModuleDestroy } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import geolite2 from 'geolite2-redist'; +import maxmind, { CityResponse, Reader } from 'maxmind'; +import QuickLRU from 'quick-lru'; + +@Injectable() +export class GeolocationService implements OnModuleDestroy { + constructor(private configService: ConfigService) {} + + private lookup: Reader | null = null; + private lru = new QuickLRU>({ + maxSize: + this.configService.get('caching.geolocationLruSize') ?? 100, + }); + + onModuleDestroy() { + if (this.lookup) this.lookup = null; + } + + /** Get the geolocation from an IP address */ + async getLocation(ipAddress: string): Promise> { + if (this.lru.has(ipAddress)) return this.lru.get(ipAddress) ?? {}; + const result = await this.getSafeLocation(ipAddress); + this.lru.set(ipAddress, result); + return result; + } + + private async getSafeLocation( + ipAddress: string, + ): Promise> { + try { + return this.getUnsafeLocation(ipAddress); + } catch (error) { + return {}; + } + } + + private async getUnsafeLocation( + ipAddress: string, + ): Promise> { + if (!this.lookup) + this.lookup = await geolite2.open('GeoLite2-City', (path) => + maxmind.open(path), + ); + return this.lookup.get(ipAddress) ?? {}; + } +} diff --git a/src/providers/github/github.module.ts b/src/providers/github/github.module.ts new file mode 100644 index 00000000..12e15308 --- /dev/null +++ b/src/providers/github/github.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { GitHubService } from './github.service'; + +@Module({ + imports: [ConfigModule], + providers: [GitHubService], + exports: [GitHubService], +}) +export class GitHubModule {} diff --git a/src/providers/github/github.service.ts b/src/providers/github/github.service.ts new file mode 100644 index 00000000..1bebe196 --- /dev/null +++ b/src/providers/github/github.service.ts @@ -0,0 +1,20 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Octokit } from '@octokit/rest'; +import { Configuration } from '../../config/configuration.interface'; + +@Injectable() +export class GitHubService { + private logger = new Logger(GitHubService.name); + octokit: Octokit; + + constructor(private configService: ConfigService) { + const config = this.configService.get('github'); + if (config.auth) + this.octokit = new Octokit({ + auth: config.auth, + userAgent: config.userAgent ?? 'staart', + }); + else this.logger.warn('GitHub API key not found'); + } +} diff --git a/src/providers/google-maps/google-maps.module.ts b/src/providers/google-maps/google-maps.module.ts new file mode 100644 index 00000000..d99e3e1a --- /dev/null +++ b/src/providers/google-maps/google-maps.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { GoogleMapsService } from './google-maps.service'; + +@Module({ + imports: [ConfigModule], + providers: [GoogleMapsService], + exports: [GoogleMapsService], +}) +export class GoogleMapsModule {} diff --git a/src/providers/google-maps/google-maps.service.ts b/src/providers/google-maps/google-maps.service.ts new file mode 100644 index 00000000..204f7b6a --- /dev/null +++ b/src/providers/google-maps/google-maps.service.ts @@ -0,0 +1,28 @@ +import { Client } from '@googlemaps/google-maps-services-js'; +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Configuration } from '../../config/configuration.interface'; + +@Injectable() +export class GoogleMapsService { + private logger = new Logger(GoogleMapsService.name); + private config = this.configService.get( + 'googleMaps', + ); + client: Client; + + constructor(private configService: ConfigService) { + if (this.config.apiKey) this.client = new Client(); + else this.logger.warn('Google Maps API key not found'); + } + + autocomplete(query: string, components?: string[]) { + return this.client.placeAutocomplete({ + params: { + input: query, + key: this.config.apiKey, + components, + }, + }); + } +} diff --git a/src/providers/mail/mail.interface.ts b/src/providers/mail/mail.interface.ts new file mode 100644 index 00000000..b31a1742 --- /dev/null +++ b/src/providers/mail/mail.interface.ts @@ -0,0 +1,5 @@ +export interface MailOptions { + template?: string; + data?: Record; + noLayout?: boolean; +} diff --git a/src/providers/mail/mail.module.ts b/src/providers/mail/mail.module.ts new file mode 100644 index 00000000..4e7ef6c4 --- /dev/null +++ b/src/providers/mail/mail.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { MailService } from './mail.service'; + +@Module({ + imports: [ConfigModule], + providers: [MailService], + exports: [MailService], +}) +export class MailModule {} diff --git a/src/providers/mail/mail.service.ts b/src/providers/mail/mail.service.ts new file mode 100644 index 00000000..b428cff8 --- /dev/null +++ b/src/providers/mail/mail.service.ts @@ -0,0 +1,94 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { render } from '@staart/mustache-markdown'; +import { SES } from 'aws-sdk'; +import { promises as fs } from 'fs'; +import mem from 'mem'; +import nodemailer from 'nodemailer'; +import Mail from 'nodemailer/lib/mailer'; +import SESTransport from 'nodemailer/lib/ses-transport'; +import PQueue from 'p-queue'; +import pRetry from 'p-retry'; +import { join } from 'path'; +import { Configuration } from '../../config/configuration.interface'; +import { MailOptions } from './mail.interface'; + +@Injectable() +export class MailService { + private readonly logger = new Logger(MailService.name); + private transport: Mail; + private config: Configuration['email']; + private queue = new PQueue({ concurrency: 1 }); + private readTemplate = mem(this.readTemplateUnmemoized); + + constructor(private configService: ConfigService) { + this.config = this.configService.get('email'); + if (this.config.ses?.accessKeyId) + this.transport = nodemailer.createTransport({ + SES: new SES({ + apiVersion: '2010-12-01', + accessKeyId: this.config.ses.accessKeyId, + secretAccessKey: this.config.ses.secretAccessKey, + region: this.config.ses.region, + }), + } as SESTransport.Options); + else this.transport = nodemailer.createTransport(this.config.transport); + } + + send(options: Mail.Options & MailOptions) { + this.queue + .add(() => + pRetry( + () => + this.sendMail({ + ...options, + from: + options.from ?? `"${this.config.name}" <${this.config.from}>`, + }), + { + retries: this.configService.get('email.retries') ?? 3, + onFailedAttempt: (error) => { + this.logger.error( + `Mail to ${options.to} failed, retrying (${error.retriesLeft} attempts left)`, + error.name, + ); + console.log(error); + }, + }, + ), + ) + .then(() => {}) + .catch(() => {}); + } + + private async sendMail(options: Mail.Options & MailOptions) { + if (options.template) { + const layout = await this.readTemplate('layout.html'); + let template = await this.readTemplate(options.template); + let [markdown, html] = render(template, options.data); + if (markdown.startsWith('#')) { + const subject = markdown.split('\n', 1)[0].replace('#', '').trim(); + if (subject) { + options.subject = options.subject ?? subject; + markdown = markdown.replace(`# ${markdown.split('\n', 1)[0]}`, ''); + } + } + options.html = options.noLayout + ? html + : render(layout, { content: html })[1]; + options.text = markdown; + options.alternatives = [ + { + contentType: 'text/x-web-markdown', + content: markdown, + }, + ]; + } + return this.transport.sendMail(options); + } + + private async readTemplateUnmemoized(name: string) { + if (!name.endsWith('.html')) name = `${name}.md`; + return fs.readFile(join('.', 'src', 'templates', name), 'utf8'); + } +} diff --git a/src/providers/prisma/prisma.interface.ts b/src/providers/prisma/prisma.interface.ts new file mode 100644 index 00000000..309e2f4a --- /dev/null +++ b/src/providers/prisma/prisma.interface.ts @@ -0,0 +1,7 @@ +export type Expose = Omit< + Omit< + Omit, 'twoFactorSecret'>, 'token'>, + 'emailSafe' + >, + 'subnet' +>; diff --git a/src/providers/prisma/prisma.module.ts b/src/providers/prisma/prisma.module.ts new file mode 100644 index 00000000..ec0ce329 --- /dev/null +++ b/src/providers/prisma/prisma.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { PrismaService } from './prisma.service'; + +@Module({ + providers: [PrismaService], + exports: [PrismaService], +}) +export class PrismaModule {} diff --git a/src/providers/prisma/prisma.service.ts b/src/providers/prisma/prisma.service.ts new file mode 100644 index 00000000..2295f147 --- /dev/null +++ b/src/providers/prisma/prisma.service.ts @@ -0,0 +1,35 @@ +import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; +import { + ApprovedSubnet, + Email, + PrismaClient, + Session, + User, +} from '@prisma/client'; +import { Expose } from './prisma.interface'; + +@Injectable() +export class PrismaService + extends PrismaClient + implements OnModuleInit, OnModuleDestroy { + async onModuleInit() { + await this.$connect(); + } + + async onModuleDestroy() { + await this.$disconnect(); + } + + /** Delete sensitive keys from an object */ + expose(item: T): Expose { + if (!item) return {} as T; + if (((item as any) as Partial).password) + (item as any).hasPassword = true; + delete ((item as any) as Partial).password; + delete ((item as any) as Partial).twoFactorSecret; + delete ((item as any) as Partial).token; + delete ((item as any) as Partial).emailSafe; + delete ((item as any) as Partial).subnet; + return item; + } +} diff --git a/src/providers/puppeteer/puppeteer.module.ts b/src/providers/puppeteer/puppeteer.module.ts new file mode 100644 index 00000000..63c21b28 --- /dev/null +++ b/src/providers/puppeteer/puppeteer.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { PuppeteerService } from './puppeteer.service'; + +@Module({ + imports: [ConfigModule], + providers: [PuppeteerService], + exports: [PuppeteerService], +}) +export class PuppeteerModule {} diff --git a/src/providers/puppeteer/puppeteer.service.ts b/src/providers/puppeteer/puppeteer.service.ts new file mode 100644 index 00000000..0e092114 --- /dev/null +++ b/src/providers/puppeteer/puppeteer.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@nestjs/common'; +import { launch, PDFOptions } from 'puppeteer'; + +@Injectable() +export class PuppeteerService { + async renderHtmlToImage( + html: string, + viewport?: { width: number; height: number }, + ) { + const browser = await launch({ + executablePath: process.env.CHROMIUM_PATH, + args: ['--no-sandbox'], + }); + const page = await browser.newPage(); + if (viewport) await page.setViewport(viewport); + await page.setContent(html, { waitUntil: 'networkidle2' }); + const screenshot = await page.screenshot({ encoding: 'binary' }); + await browser.close(); + return screenshot; + } + + async renderHtmlToPdf( + html: string, + viewport?: { width: number; height: number }, + options?: PDFOptions, + ) { + const browser = await launch({ + executablePath: process.env.CHROMIUM_PATH, + args: ['--no-sandbox'], + }); + const page = await browser.newPage(); + if (viewport) await page.setViewport(viewport); + await page.setContent(html, { waitUntil: 'networkidle2' }); + const file = await page.pdf(options); + await browser.close(); + return file; + } +} diff --git a/src/providers/pwned/pwned.module.ts b/src/providers/pwned/pwned.module.ts new file mode 100644 index 00000000..25aca6ff --- /dev/null +++ b/src/providers/pwned/pwned.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { PwnedService } from './pwned.service'; + +@Module({ + providers: [PwnedService], + exports: [PwnedService], +}) +export class PwnedModule {} diff --git a/src/providers/pwned/pwned.service.spec.ts b/src/providers/pwned/pwned.service.spec.ts new file mode 100644 index 00000000..4e3defc7 --- /dev/null +++ b/src/providers/pwned/pwned.service.spec.ts @@ -0,0 +1,19 @@ +import { PwnedService } from './pwned.service'; +import { randomBytes } from 'crypto'; + +const pwnedService = new PwnedService(); + +describe('PwnedService', () => { + describe('isPasswordSafe', () => { + it('unsafe password', async () => { + const safe = await pwnedService.isPasswordSafe('password123'); + expect(safe).toBeFalsy(); + }); + it('safe password', async () => { + const safe = await pwnedService.isPasswordSafe( + randomBytes(10).toString(), + ); + expect(safe).toBeTruthy(); + }); + }); +}); diff --git a/src/providers/pwned/pwned.service.ts b/src/providers/pwned/pwned.service.ts new file mode 100644 index 00000000..e5871006 --- /dev/null +++ b/src/providers/pwned/pwned.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@nestjs/common'; +import { pwnedPassword } from 'hibp'; + +@Injectable() +export class PwnedService { + async isPasswordSafe(password: string): Promise { + try { + const numberOfPwned = await this.unsafeCheckPwnedPassword(password); + return !numberOfPwned; + } catch (error) { + return true; + } + } + + private async unsafeCheckPwnedPassword(password: string): Promise { + return pwnedPassword(password); + } +} diff --git a/src/providers/s3/s3.module.ts b/src/providers/s3/s3.module.ts new file mode 100644 index 00000000..818216ee --- /dev/null +++ b/src/providers/s3/s3.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { S3Service } from './s3.service'; + +@Module({ + imports: [ConfigModule], + providers: [S3Service], + exports: [S3Service], +}) +export class S3Module {} diff --git a/src/providers/s3/s3.service.ts b/src/providers/s3/s3.service.ts new file mode 100644 index 00000000..d2685c0d --- /dev/null +++ b/src/providers/s3/s3.service.ts @@ -0,0 +1,96 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { AWSError, S3 } from 'aws-sdk'; +import { Configuration } from '../../config/configuration.interface'; + +@Injectable() +export class S3Service { + client?: S3; + private logger = new Logger(S3Service.name); + + constructor(private configService: ConfigService) { + const config = this.configService.get('s3'); + if (config.accessKeyId) + this.client = new S3({ + apiVersion: '2006-03-01', + accessKeyId: config.accessKeyId, + secretAccessKey: config.secretAccessKey, + region: config.region, + }); + else this.logger.warn('No S3 API key set'); + } + + /** Get a signed URL to access an S3 object for 5 minutes */ + signedUrl(bucket: string, key: string): Promise { + return new Promise((resolve, reject) => { + this.client.getSignedUrl( + 'getObject', + { + Bucket: bucket, + Key: key, + Expires: 300, + }, + (error: AWSError, data: string) => { + if (error) return reject(error); + resolve(data); + }, + ); + }); + } + + /** Get a policy to upload to S3 directly */ + postPolicy(bucket: string, key: string): Promise { + return new Promise((resolve, reject) => { + this.client.createPresignedPost( + { + Bucket: bucket, + Fields: { + key, + }, + Expires: 300, + }, + (error: AWSError, data: S3.PresignedPost) => { + if (error) return reject(error); + resolve(data); + }, + ); + }); + } + + upload( + name: string, + body: Buffer, + bucket?: string, + publicRead?: true, + ): Promise { + return new Promise((resolve, reject) => { + this.client.upload( + { + Bucket: bucket, + Key: name, + Body: body, + ACL: publicRead ? 'public-read' : undefined, + }, + (error: AWSError, data: S3.ManagedUpload.SendData) => { + if (error) return reject(error); + resolve(data); + }, + ); + }); + } + + get(bucket: string, name: string): Promise { + return new Promise((resolve, reject) => { + this.client.getObject( + { + Bucket: bucket, + Key: name, + }, + (error: AWSError, data: S3.Types.GetObjectOutput) => { + if (error) return reject(error); + resolve(data); + }, + ); + }); + } +} diff --git a/src/providers/slack/slack.module.ts b/src/providers/slack/slack.module.ts new file mode 100644 index 00000000..a8ab1665 --- /dev/null +++ b/src/providers/slack/slack.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { SlackService } from './slack.service'; + +@Module({ + imports: [ConfigModule], + providers: [SlackService], + exports: [SlackService], +}) +export class SlackModule {} diff --git a/src/providers/slack/slack.service.ts b/src/providers/slack/slack.service.ts new file mode 100644 index 00000000..624fdc56 --- /dev/null +++ b/src/providers/slack/slack.service.ts @@ -0,0 +1,74 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { + ChatPostMessageArguments, + WebAPICallResult, + WebClient, +} from '@slack/web-api'; +import PQueue from 'p-queue'; +import pRetry from 'p-retry'; +import { Configuration } from '../../config/configuration.interface'; + +@Injectable() +export class SlackService { + client?: WebClient; + private logger = new Logger(SlackService.name); + private queue = new PQueue({ concurrency: 1 }); + + constructor(private configService: ConfigService) { + const config = this.configService.get('slack'); + if (config.token) + this.client = new WebClient(config.token, { + slackApiUrl: config.slackApiUrl, + rejectRateLimitedCalls: config.rejectRateLimitedCalls, + }); + } + + send(options: ChatPostMessageArguments) { + this.queue + .add(() => + pRetry(() => this.sendMessage(options), { + retries: this.configService.get('slack.retries') ?? 3, + onFailedAttempt: (error) => { + this.logger.error( + `Message to ${options.channel} failed, retrying (${error.retriesLeft} attempts left)`, + error.name, + ); + }, + }), + ) + .then(() => {}) + .catch(() => {}); + } + + sendToChannel(channelName: string, text: string) { + this.queue + .add(() => + pRetry(() => this.sendMessageToChannel(channelName, text), { + retries: this.configService.get('slack.retries') ?? 3, + onFailedAttempt: (error) => { + this.logger.error( + `Message to ${channelName} failed, retrying (${error.retriesLeft} attempts left)`, + error.name, + ); + }, + }), + ) + .then(() => {}) + .catch(() => {}); + } + + private async sendMessageToChannel(channelName: string, text: string) { + const conversations = (await this.client?.conversations.list()) as WebAPICallResult & { + channels: { name: string; id: string }[]; + }; + const channel = conversations.channels.find( + (channel) => channel.name === channelName, + ); + const options: ChatPostMessageArguments = { text, channel: channel.id }; + return this.client?.chat.postMessage(options); + } + private async sendMessage(options: ChatPostMessageArguments) { + return this.client?.chat.postMessage(options); + } +} diff --git a/src/providers/tasks/tasks.module.ts b/src/providers/tasks/tasks.module.ts new file mode 100644 index 00000000..9920896a --- /dev/null +++ b/src/providers/tasks/tasks.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DomainsModule } from '../../modules/domains/domains.module'; +import { ElasticSearchModule } from '../elasticsearch/elasticsearch.module'; +import { PrismaModule } from '../prisma/prisma.module'; +import { TasksService } from './tasks.service'; +import { UsersModule } from '../../modules/users/users.module'; +import { MetricsModule } from '../../modules/metrics/metrics.module'; + +@Module({ + imports: [ + ConfigModule, + PrismaModule, + ElasticSearchModule, + DomainsModule, + UsersModule, + MetricsModule, + ], + providers: [TasksService], + exports: [TasksService], +}) +export class TasksModule {} diff --git a/src/providers/tasks/tasks.service.ts b/src/providers/tasks/tasks.service.ts new file mode 100644 index 00000000..5163ea41 --- /dev/null +++ b/src/providers/tasks/tasks.service.ts @@ -0,0 +1,95 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { Configuration } from '../../config/configuration.interface'; +import { DomainsService } from '../../modules/domains/domains.service'; +import { MetricsService } from '../../modules/metrics/metrics.service'; +import { UsersService } from '../../modules/users/users.service'; +import { ElasticSearchService } from '../elasticsearch/elasticsearch.service'; +import { PrismaService } from '../prisma/prisma.service'; + +@Injectable() +export class TasksService { + constructor( + private prisma: PrismaService, + private configService: ConfigService, + private elasticSearchService: ElasticSearchService, + private domainsService: DomainsService, + private usersService: UsersService, + private metricsService: MetricsService, + ) {} + private readonly logger = new Logger(TasksService.name); + + @Cron(CronExpression.EVERY_MINUTE) + async updateMetrics() { + await this.metricsService.updateProcessMetrics(); + } + + @Cron(CronExpression.EVERY_DAY_AT_1PM) + async deleteOldSessions() { + const now = new Date(); + const unusedRefreshTokenExpiryDays = + this.configService.get('security.unusedRefreshTokenExpiryDays') ?? + 30; + now.setDate(now.getDate() - unusedRefreshTokenExpiryDays); + const deleted = await this.prisma.session.deleteMany({ + where: { updatedAt: { lte: now } }, + }); + if (deleted.count) + this.logger.debug(`Deleted ${deleted.count} expired sessions`); + } + + @Cron(CronExpression.EVERY_DAY_AT_2PM) + async deleteInactiveUsers() { + const now = new Date(); + const inactiveUserDeleteDays = + this.configService.get('security.inactiveUserDeleteDays') ?? 30; + now.setDate(now.getDate() - inactiveUserDeleteDays); + const deleted = await this.prisma.user.findMany({ + select: { id: true }, + where: { + active: false, + sessions: { every: { updatedAt: { lte: now } } }, + }, + }); + if (deleted.length) { + for await (const user of deleted) + await this.usersService.deleteUser(user.id); + this.logger.debug(`Deleted ${deleted.length} inactive users`); + } + } + + @Cron(CronExpression.EVERY_DAY_AT_3PM) + async deleteOldLogs() { + const tracking = this.configService.get( + 'tracking', + ); + if (tracking.deleteOldLogs) + return this.elasticSearchService.deleteOldRecords( + tracking.index, + tracking.deleteOldLogsDays, + ); + } + + @Cron(CronExpression.EVERY_DAY_AT_5PM) + async verifyDomains() { + const domains = await this.prisma.domain.findMany({ + where: { isVerified: false }, + }); + for await (const domain of domains) { + try { + await this.domainsService.verifyDomain(domain.groupId, domain.id); + } catch (error) {} + } + } + + @Cron(CronExpression.EVERY_DAY_AT_6PM) + async deleteOldAuditLogs() { + const now = new Date(); + now.setDate(now.getDate() - 90); + const deleted = await this.prisma.auditLog.deleteMany({ + where: { createdAt: { lte: now } }, + }); + if (deleted.count) this.logger.debug(`Deleted ${deleted.count} audit logs`); + } +} diff --git a/src/providers/tokens/tokens.constants.ts b/src/providers/tokens/tokens.constants.ts new file mode 100644 index 00000000..886a0b22 --- /dev/null +++ b/src/providers/tokens/tokens.constants.ts @@ -0,0 +1,7 @@ +export const MULTI_FACTOR_TOKEN = 'MULTI_FACTOR_TOKEN'; +export const PASSWORD_RESET_TOKEN = 'PASSWORD_RESET_TOKEN'; +export const EMAIL_VERIFY_TOKEN = 'EMAIL_VERIFY_TOKEN'; +export const APPROVE_SUBNET_TOKEN = 'APPROVE_SUBNET_TOKEN'; +export const EMAIL_MFA_TOKEN = 'EMAIL_MFA_TOKEN'; +export const LOGIN_ACCESS_TOKEN = 'LOGIN_ACCESS_TOKEN'; +export const MERGE_ACCOUNTS_TOKEN = 'MERGE_ACCOUNTS_TOKEN'; diff --git a/src/providers/tokens/tokens.module.ts b/src/providers/tokens/tokens.module.ts new file mode 100644 index 00000000..cf0e29f9 --- /dev/null +++ b/src/providers/tokens/tokens.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TokensService } from './tokens.service'; + +@Module({ + imports: [ConfigModule], + providers: [TokensService], + exports: [TokensService], +}) +export class TokensModule {} diff --git a/src/providers/tokens/tokens.service.spec.ts b/src/providers/tokens/tokens.service.spec.ts new file mode 100644 index 00000000..958cdcd0 --- /dev/null +++ b/src/providers/tokens/tokens.service.spec.ts @@ -0,0 +1,66 @@ +import { ConfigService } from '@nestjs/config'; +import { sign } from 'jsonwebtoken'; +import { TokensService } from './tokens.service'; + +const tokensService = new TokensService( + new ConfigService({ security: { jwtSecret: 'example' } }), +); + +describe('TokensService', () => { + describe('generateUuid', () => { + it('generated UUID of length', async () => { + const uuid = tokensService.generateUuid(); + expect(uuid.length).toBe(36); + }); + it('generated valid UUID', async () => { + const uuid = tokensService.generateUuid(); + expect(uuid).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i, + ); + }); + }); + + describe('signJwt', () => { + it('signs a JWT of length', async () => { + const jwt = tokensService.signJwt('SUBJECT', { ok: true }, '1d', {}); + expect(jwt.length).toBe(163); + }); + it('signs a valid JWT', async () => { + const jwt = tokensService.signJwt('SUBJECT', { ok: true }, '1d', {}); + expect(jwt).toMatch( + /^([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_\-\+\/=]*)/, + ); + }); + }); + + describe('verify', () => { + it('verifies a JWT', async () => { + const jwt = tokensService.signJwt('SUBJECT', { ok: true }, '1d', {}); + const verified = tokensService.verify('SUBJECT', jwt); + expect(verified).toBeDefined(); + }); + it('verifies a JWT and gets data', async () => { + const jwt = tokensService.signJwt('SUBJECT', { ok: true }, '1d', {}); + const verified = tokensService.verify<{ ok: boolean }>('SUBJECT', jwt); + expect(verified.ok).toBeTruthy(); + }); + }); + + describe('decode', () => { + it('decodes a JWT', async () => { + const jwt = tokensService.signJwt('SUBJECT', { ok: true }, '1d', {}); + const verified = tokensService.decode(jwt); + expect(verified).toBeDefined(); + }); + it('decodes a JWT and gets data', async () => { + const jwt = tokensService.signJwt('SUBJECT', { ok: true }, '1d', {}); + const verified = tokensService.decode<{ ok: boolean }>(jwt); + expect(verified.ok).toBeTruthy(); + }); + it('decodes a JWT and with a wrong secret', async () => { + const jwt = sign({ ok: true }, 'another-secret'); + const verified = tokensService.decode<{ ok: boolean }>(jwt); + expect(verified.ok).toBeTruthy(); + }); + }); +}); diff --git a/src/providers/tokens/tokens.service.ts b/src/providers/tokens/tokens.service.ts new file mode 100644 index 00000000..80caedbf --- /dev/null +++ b/src/providers/tokens/tokens.service.ts @@ -0,0 +1,112 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { async as cryptoRandomString } from 'crypto-random-string'; +import { + decode, + DecodeOptions, + sign, + SignOptions, + verify, + VerifyOptions, +} from 'jsonwebtoken'; +import { v4 } from 'uuid'; +import { INVALID_TOKEN } from '../../errors/errors.constants'; + +@Injectable() +export class TokensService { + constructor(private configService: ConfigService) {} + + /** + * Sign a JWT + * @param subject - Subject + * @param payload - Object payload + * @param expiresIn - Expiry string (vercel/ms) + * @param options - Signing options + */ + signJwt( + subject: string, + payload: number | string | object | Buffer, + expiresIn?: string, + options?: SignOptions, + ) { + if (typeof payload === 'number') payload = payload.toString(); + return sign( + payload, + this.configService.get('security.jwtSecret') ?? '', + { + ...options, + subject, + expiresIn, + }, + ); + } + + /** + * Verify and decode a JWT + * @param subject - Subject + * @param token - JWT + * @param options - Verify options + */ + verify(subject: string, token: string, options?: VerifyOptions) { + try { + return (verify( + token, + this.configService.get('security.jwtSecret') ?? '', + { ...options, subject }, + ) as any) as T; + } catch (error) { + throw new UnauthorizedException(INVALID_TOKEN); + } + } + + /** + * Decode a JWT without verifying it + * @deprecated Use verify() instead + * @param token - JWT + * @param options - Decode options + */ + decode(token: string, options?: DecodeOptions) { + return decode(token, options) as T; + } + + /** + * Generate a UUID + */ + generateUuid() { + return v4(); + } + + /** + * Generate a cryptographically strong random string + * @param length - Length of returned string + * @param charactersOrType - Characters or one of the supported types + */ + async generateRandomString( + length = 32, + charactersOrType = 'alphanumeric', + ): Promise { + if ( + [ + 'hex', + 'base64', + 'url-safe', + 'numeric', + 'distinguishable', + 'ascii-printable', + 'alphanumeric', + ].includes(charactersOrType) + ) + return cryptoRandomString({ + length, + type: charactersOrType as + | 'hex' + | 'base64' + | 'url-safe' + | 'numeric' + | 'distinguishable' + | 'ascii-printable' + | 'alphanumeric', + }); + return cryptoRandomString({ length, characters: charactersOrType }); + } +} diff --git a/src/providers/twilio/twilio.module.ts b/src/providers/twilio/twilio.module.ts new file mode 100644 index 00000000..5adc195f --- /dev/null +++ b/src/providers/twilio/twilio.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TwilioService } from './twilio.service'; + +@Module({ + imports: [ConfigModule], + providers: [TwilioService], + exports: [TwilioService], +}) +export class TwilioModule {} diff --git a/src/providers/twilio/twilio.service.ts b/src/providers/twilio/twilio.service.ts new file mode 100644 index 00000000..9cba04a1 --- /dev/null +++ b/src/providers/twilio/twilio.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import PQueue from 'p-queue'; +import pRetry from 'p-retry'; +import twilio from 'twilio'; +import { MessageListInstanceCreateOptions } from 'twilio/lib/rest/api/v2010/account/message'; +import TwilioClient from 'twilio/lib/rest/Twilio'; + +@Injectable() +export class TwilioService { + client: TwilioClient; + logger = new Logger(TwilioService.name); + private queue = new PQueue({ concurrency: 1 }); + + constructor(private configService: ConfigService) { + const twilioAccountSid = this.configService.get( + 'sms.twilioAccountSid', + ); + const twilioAuthToken = this.configService.get( + 'sms.twilioAuthToken', + ); + if (!twilioAccountSid || !twilioAuthToken) + this.logger.warn('Twilio account SID/auth token not found'); + this.client = twilio( + twilioAccountSid || 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + twilioAuthToken || 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + ); + } + + send(options: MessageListInstanceCreateOptions) { + this.queue + .add(() => + pRetry(() => this.sendSms(options), { + retries: this.configService.get('sms.retries') ?? 3, + onFailedAttempt: (error) => { + this.logger.error( + `SMS to ${options.to} failed, retrying (${error.retriesLeft} attempts left)`, + error.name, + ); + }, + }), + ) + .then(() => {}) + .catch(() => {}); + } + + private async sendSms(options: MessageListInstanceCreateOptions) { + return this.client.messages.create(options); + } +} diff --git a/src/templates/auth/approve-subnet.md b/src/templates/auth/approve-subnet.md new file mode 100644 index 00000000..90626aa8 --- /dev/null +++ b/src/templates/auth/approve-subnet.md @@ -0,0 +1,9 @@ +# Approve your login + +Hi {{name}}, + +Someone (hopefully you) logged in to your account from a new location ({{ locationName }}), so you'll have to approve it. + +Approve this login + +Note that this link is valid for {{ minutes }} minutes only. If you didn't request this email, you can just ignore it; we won't give anyone else access to your account. diff --git a/src/templates/auth/email-verification.md b/src/templates/auth/email-verification.md new file mode 100644 index 00000000..991e7eb3 --- /dev/null +++ b/src/templates/auth/email-verification.md @@ -0,0 +1,9 @@ +# Verify your email + +Hi {{name}}, + +Someone (hopefully you) requested a link to confirm your email, so here you go. + +Verify my email + +Note that this link is valid for {{ days }} days only. If you didn't request this email, you can just ignore it. diff --git a/src/templates/auth/enable-email-mfa.md b/src/templates/auth/enable-email-mfa.md new file mode 100644 index 00000000..8ab4c947 --- /dev/null +++ b/src/templates/auth/enable-email-mfa.md @@ -0,0 +1,7 @@ +# Enable multi-factor authentication with {{ code }} + +Hi {{name}}, + +Enter the following code to enable email-based multi-factor authentication: **{{ code }}**. + +If you didn't request this email, you can just ignore it; we won't give anyone else access to your account. diff --git a/src/templates/auth/login-link.md b/src/templates/auth/login-link.md new file mode 100644 index 00000000..65c85344 --- /dev/null +++ b/src/templates/auth/login-link.md @@ -0,0 +1,9 @@ +# Login to your account + +Hi {{name}}, + +You can click on the link below to log in to your account. + +Login to your account + +Note that this link is valid for {{ minutes }} minutes only. If you didn't request this email, you can just ignore it; we won't give anyone else access to your account. diff --git a/src/templates/auth/password-reset.md b/src/templates/auth/password-reset.md new file mode 100644 index 00000000..2698ac60 --- /dev/null +++ b/src/templates/auth/password-reset.md @@ -0,0 +1,9 @@ +# Reset your password + +Hi {{name}}, + +Someone (hopefully you) requested a link to reset your password, so here you go. + +Reset my password + +Note that this link is valid for {{ minutes }} minutes only. If you didn't request this email, you can just ignore it; we won't give anyone else access to your account. diff --git a/src/templates/auth/resend-email-verification.md b/src/templates/auth/resend-email-verification.md new file mode 100644 index 00000000..ccad33c8 --- /dev/null +++ b/src/templates/auth/resend-email-verification.md @@ -0,0 +1,9 @@ +# Verify your email + +Hi {{name}}, + +Someone (hopefully you) requested to another link to confirm your email, so here you go. + +Verify my email + +Note that this link is valid for {{ days }} days only. If you didn't request this email, you can just ignore it. diff --git a/src/templates/auth/used-backup-code.md b/src/templates/auth/used-backup-code.md new file mode 100644 index 00000000..7f55b4e2 --- /dev/null +++ b/src/templates/auth/used-backup-code.md @@ -0,0 +1,9 @@ +# Login with a backup code + +Hi {{name}}, + +Someone (hopefully you) logged in to your account from **{{ locationName }}**, and they used a backup code when asked for a two-factor authentication code. + +Check recent activity + +If you didn't login to your account, you should immediately change your password and regenerate your backup codes. You can do that by logging in to your account. diff --git a/src/templates/groups/invitation.md b/src/templates/groups/invitation.md new file mode 100644 index 00000000..cff5c06b --- /dev/null +++ b/src/templates/groups/invitation.md @@ -0,0 +1,9 @@ +# Welcome to {{ group }} + +Hi {{name}}, + +You've joined the group **{{ group }}**. You can manage your membership by logging to your account. + +Visit this group + +If you didn't want to be added to {{ group }}, you can log in to your account and leave the group. diff --git a/src/templates/layout.html b/src/templates/layout.html new file mode 100644 index 00000000..3eb2f002 --- /dev/null +++ b/src/templates/layout.html @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + +
  +
+ + + + +
+ + + + +
{{{ content }}}
+
+
+
 
+ + diff --git a/src/templates/users/deactivated.md b/src/templates/users/deactivated.md new file mode 100644 index 00000000..f59d763c --- /dev/null +++ b/src/templates/users/deactivated.md @@ -0,0 +1,7 @@ +# Account deactivated + +Hi {{name}}, + +Your account has been deactivated. + +If you didn't want to deactivate your account, you can log in within 30 days to reactivate it. Otherwise, it will be permanently deleted after 30 days. diff --git a/src/templates/users/merge-request.md b/src/templates/users/merge-request.md new file mode 100644 index 00000000..26e05eca --- /dev/null +++ b/src/templates/users/merge-request.md @@ -0,0 +1,9 @@ +# Account merge request + +Hi {{name}}, + +We received a request to merge your account with another account. + +Merge this account + +Note that this link is valid for {{ minutes }} minutes only. If you didn't request this email, you can just ignore it; we won't give anyone else access to your account. diff --git a/src/templates/users/password-changed.md b/src/templates/users/password-changed.md new file mode 100644 index 00000000..e776d9db --- /dev/null +++ b/src/templates/users/password-changed.md @@ -0,0 +1,7 @@ +# Password changed + +Hi {{name}}, + +Your password has been changed. + +If you didn't change your password, you should immediately reset your password. diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 00000000..1f53798b --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/tests/app.e2e-spec.ts b/tests/app.e2e-spec.ts new file mode 100644 index 00000000..a547cf2c --- /dev/null +++ b/tests/app.e2e-spec.ts @@ -0,0 +1,77 @@ +import { INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import request from 'supertest'; +import { AppModule } from './../src/app.module'; +import { randomBytes } from 'crypto'; +import { PrismaClient } from '@prisma/client'; +import type { User, ApiKey } from '@prisma/client'; + +const email = 'staart@anandchowdhary.com'; +const password = randomBytes(10).toString(); + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('gets /', () => request(app.getHttpServer()).get('/').expect(302)); + + let tokens: + | { accessToken: string; refreshToken: string } + | undefined = undefined; + let userId: number | undefined = undefined; + let apiKeyStr: string | undefined = undefined; + + it('registers and logs in', async () => { + const registerResponse = await request(app.getHttpServer()) + .post('/auth/register') + .set('Accept', 'application/json') + .send({ name: 'Koj Bot', email, password }) + .expect(201); + const { id }: User = registerResponse.body; + userId = id; + + const loginResponse = await request(app.getHttpServer()) + .post('/auth/login') + .set('Accept', 'application/json') + .send({ email, password }) + .expect(201); + tokens = loginResponse.body; + }); + + it('gets user details', () => + request(app.getHttpServer()) + .get(`/users/${userId}`) + .set('Accept', 'application/json') + .set('Authorization', `Bearer ${tokens.accessToken}`) + .expect(200)); + + it('creates and uses API key', async () => { + const apiKeyResponse = await request(app.getHttpServer()) + .post(`/users/${userId}/api-keys`) + .set('Accept', 'application/json') + .set('Authorization', `Bearer ${tokens.accessToken}`) + .send({ name: 'Example API key', scopes: [`user-${userId}:read-info`] }) + .expect(201); + const { apiKey }: ApiKey = apiKeyResponse.body; + apiKeyStr = apiKey; + + await request(app.getHttpServer()) + .get(`/users/${userId}`) + .set('Accept', 'application/json') + .set('X-Api-Key', apiKeyStr) + .expect(200); + }); + + afterAll(async () => { + const client = new PrismaClient(); + await client.user.deleteMany({ where: { emails: { some: { email } } } }); + }); +}); diff --git a/tests/jest-e2e.json b/tests/jest-e2e.json new file mode 100644 index 00000000..e9d912f3 --- /dev/null +++ b/tests/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/tests/test-before.ts b/tests/test-before.ts new file mode 100644 index 00000000..0aecbb4d --- /dev/null +++ b/tests/test-before.ts @@ -0,0 +1,20 @@ +import { execSync } from 'child_process'; +import { pathExists, writeFile } from 'fs-extra'; +import { join } from 'path'; + +export const beforeRunningApp = async () => { + // Write a test .env file + const hasDotEnv = await pathExists(join('.', '.env')); + if (!hasDotEnv) + await writeFile( + join('.', '.env'), + ` +DATABASE_URL = "mysql://root:@127.0.0.1:${process.env.DB_PORT || 3306}/api-test" + +`, + ); + + if (!hasDotEnv) + console.log(execSync('npx prisma db push --preview-feature').toString()); +}; +beforeRunningApp(); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..64f86c6b --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..3a4985d5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true + } +}