diff --git a/.github/workflows/test-example-applications.yaml b/.github/workflows/test-example-applications.yaml new file mode 100644 index 00000000..c7d72c64 --- /dev/null +++ b/.github/workflows/test-example-applications.yaml @@ -0,0 +1,73 @@ +name: Testing Example Applications + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + project: ["express-pg-auth0", "express-pg-jwt", "nest-mongo-auth0", "nest-mongo-jwt", "nest-pg-auth0", "nest-pg-jwt"] + max-parallel: 1 + + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + ports: ["5432:5432"] + + mongo: + image: mongo:latest + ports: ["27017:27017"] + env: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: pass@ord + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Use Node.js + uses: actions/setup-node@v2 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + working-directory: ${{ matrix.project }} + + - name: Create .env.test file + run: | + mv .env.example .env.test + + sed -i 's/NODE_ENV=.*/NODE_ENV=test/g' .env.test + sed -i 's/PORT=.*/PORT=3000/g' .env.test + + sed -i 's/ERROR_LOGGING=.*/ERROR_LOGGING=false/g' .env.test + sed -i 's/REQUEST_LOGGING=.*/REQUEST_LOGGING=false/g' .env.test + sed -i 's/SWAGGER=.*/SWAGGER=false/g' .env.test + + sed -i 's/PGHOST=.*/PGHOST=localhost/g' .env.test + sed -i 's/PGPORT=.*/PGPORT=5432/g' .env.test + sed -i 's/PGUSER=.*/PGUSER=postgres/g' .env.test + sed -i 's/PGPASSWORD=.*/PGPASSWORD=postgres/g' .env.test + sed -i 's/PGDATABASE=.*/PGDATABASE=test/g' .env.test + + sed -i 's/MONGO_PROTOCOL=.*/MONGO_PROTOCOL=mongodb/g' .env.test + sed -i 's/MONGO_HOST=.*/MONGO_HOST=localhost/g' .env.test + sed -i 's/MONGO_PORT=.*/MONGO_PORT=27017/g' .env.test + sed -i 's/MONGO_USER=.*/MONGO_USER=root/g' .env.test + sed -i 's/MONGO_PASSWORD=.*/MONGO_PASSWORD=pass@ord/g' .env.test + sed -i 's/MONGO_DATABASE_NAME=.*/MONGO_DATABASE_NAME=example-app/g' .env.test + working-directory: ${{ matrix.project }} + + - name: Run tests + run: npm run test + working-directory: ${{ matrix.project }} + + - name: Run end-to-end tests + run: npm run test:e2e + working-directory: ${{ matrix.project }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 430355d7..626a9900 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ build .vscode .eslintcache .prettiercache +.env +.env.test diff --git a/assets/express/example-app/jest.setup.ts b/assets/express/example-app/jest.setup.ts deleted file mode 100644 index 07f5b98a..00000000 --- a/assets/express/example-app/jest.setup.ts +++ /dev/null @@ -1,2 +0,0 @@ -import '@extensions/zod/register'; -import '@extensions/knex/register'; diff --git a/assets/express/example-app/test/jest.setup.ts b/assets/express/example-app/test/jest.setup.ts deleted file mode 100644 index 07f5b98a..00000000 --- a/assets/express/example-app/test/jest.setup.ts +++ /dev/null @@ -1,2 +0,0 @@ -import '@extensions/zod/register'; -import '@extensions/knex/register'; diff --git a/assets/nest/example-app-pg/jest.setup.ts b/assets/nest/example-app-pg/jest.setup.ts deleted file mode 100644 index 437c4488..00000000 --- a/assets/nest/example-app-pg/jest.setup.ts +++ /dev/null @@ -1,2 +0,0 @@ -import '@database/extensions/knex/register'; -import 'reflect-metadata'; diff --git a/assets/.commitlintrc.js b/express-pg-auth0/.commitlintrc.js similarity index 100% rename from assets/.commitlintrc.js rename to express-pg-auth0/.commitlintrc.js diff --git a/assets/.czrc b/express-pg-auth0/.czrc similarity index 100% rename from assets/.czrc rename to express-pg-auth0/.czrc diff --git a/assets/.dockerignore b/express-pg-auth0/.dockerignore similarity index 100% rename from assets/.dockerignore rename to express-pg-auth0/.dockerignore diff --git a/.editorconfig b/express-pg-auth0/.editorconfig similarity index 100% rename from .editorconfig rename to express-pg-auth0/.editorconfig diff --git a/express-pg-auth0/.env.example b/express-pg-auth0/.env.example new file mode 100644 index 00000000..112d2ef3 --- /dev/null +++ b/express-pg-auth0/.env.example @@ -0,0 +1,29 @@ +# Node +NODE_ENV=development + +# HTTP +PORT=3000 + +# Logging +ERROR_LOGGING=true +REQUEST_LOGGING=true + +# Swagger +SWAGGER=true + +# Knex +DEBUG=knex:query + +# PostgreSQL +PGHOST=localhost +PGPORT=5432 +PGUSER=user +PGPASSWORD=password +PGDATABASE=default + +# AUTH0 +AUTH0_ISSUER_URL=https://mock.auth0.com/api/v2/ +AUTH0_CLIENT_ID=CLIENT_ID +AUTH0_AUDIENCE=AUDIENCE +AUTH0_CLIENT_SECRET=CLIENT_SECRET + diff --git a/assets/.eslintignore b/express-pg-auth0/.eslintignore similarity index 100% rename from assets/.eslintignore rename to express-pg-auth0/.eslintignore diff --git a/express-pg-auth0/.eslintrc.js b/express-pg-auth0/.eslintrc.js new file mode 100644 index 00000000..4c17f68c --- /dev/null +++ b/express-pg-auth0/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + env: { + commonjs: false, + es2021: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:security/recommended', + 'prettier', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: {}, +}; diff --git a/assets/.github/workflows/audit.yaml b/express-pg-auth0/.github/workflows/audit.yaml similarity index 100% rename from assets/.github/workflows/audit.yaml rename to express-pg-auth0/.github/workflows/audit.yaml diff --git a/assets/.github/workflows/coverage-e2e.yaml b/express-pg-auth0/.github/workflows/coverage-e2e.yaml similarity index 100% rename from assets/.github/workflows/coverage-e2e.yaml rename to express-pg-auth0/.github/workflows/coverage-e2e.yaml diff --git a/assets/.github/workflows/coverage.yaml b/express-pg-auth0/.github/workflows/coverage.yaml similarity index 100% rename from assets/.github/workflows/coverage.yaml rename to express-pg-auth0/.github/workflows/coverage.yaml diff --git a/assets/git/gitignorefile b/express-pg-auth0/.gitignore similarity index 100% rename from assets/git/gitignorefile rename to express-pg-auth0/.gitignore diff --git a/assets/.husky/gitignorefile b/express-pg-auth0/.husky/.gitignore similarity index 100% rename from assets/.husky/gitignorefile rename to express-pg-auth0/.husky/.gitignore diff --git a/assets/.husky/commit-msg b/express-pg-auth0/.husky/commit-msg similarity index 100% rename from assets/.husky/commit-msg rename to express-pg-auth0/.husky/commit-msg diff --git a/express-pg-auth0/.husky/pre-commit b/express-pg-auth0/.husky/pre-commit new file mode 100755 index 00000000..2151bed3 --- /dev/null +++ b/express-pg-auth0/.husky/pre-commit @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- tsc --noEmit +npx --no -- lint-staged +npx --no -- ls-lint +npm run lint:dockerfile +npm run license:check +npm run test:cov +npm run test:e2e:cov +scripts/detect-secrets.sh + +# Enable branch naming validation by uncommenting the lines below and adjust valid_branch_regex to your preference +# # Enforce branch naming conventions +# LC_ALL=C + +# # Git branch will return an empty line before the initial commit +# git_branch=$(git branch) + +# if [ -z "$git_branch" ]; then +# echo "Initial commit, no branch name linting" +# exit 0 +# fi + +# # INSERT A REGEX PATTERN ACCORDING TO YOUR PROJECT BRANCH NAMING CONVENTION. +# branch_regex="^(chore|feat|fix|docs|style|refactor|test|revert|ci)\/[a-z0-9._-]+$" + +# # Get the branch name +# branch="$(git rev-parse --abbrev-ref HEAD)" + +# # UPDATE THE EXAMPLES TO MATCH YOUR PROJECT BRANCH NAMING CONVENTION. +# error_message="Branch name validation failed. Branch name must adhere to: /$branch_regex/. +# You should rename your branch to a valid name and try again. Here are some examples of valid branch names: + +# feat/my-feature +# test/my-feature +# fix/that-bug +# chore/my-task +# docs/update-readme +# refactor/do-thing-differently +# ci/update-build-script +# " + + +# # Use grep to do the matching, as `if [[ ]]` is not available in `sh` +# match=$(echo $branch | grep -E "$branch_regex") || echo "" + +# # Check if the match is a non-empty string +# if [ -z "$match" ]; then +# echo "$error_message" +# exit 1 +# fi diff --git a/assets/.husky/pre-push b/express-pg-auth0/.husky/pre-push similarity index 100% rename from assets/.husky/pre-push rename to express-pg-auth0/.husky/pre-push diff --git a/express-pg-auth0/.lintstagedrc b/express-pg-auth0/.lintstagedrc new file mode 100644 index 00000000..22e14328 --- /dev/null +++ b/express-pg-auth0/.lintstagedrc @@ -0,0 +1,12 @@ +{ + "*.{js,cjs,mjs,ts}": [ + "prettier --write", + "eslint --fix" + ], + "*.md": [ + "prettier --write", + "markdownlint --fix" + ], + "*.sh": "shellcheck -x", + "package.json": "sort-package-json" +} \ No newline at end of file diff --git a/assets/express/example-app-auth0/.ls-lint.yml b/express-pg-auth0/.ls-lint.yml similarity index 100% rename from assets/express/example-app-auth0/.ls-lint.yml rename to express-pg-auth0/.ls-lint.yml diff --git a/assets/.nvmrc b/express-pg-auth0/.nvmrc similarity index 100% rename from assets/.nvmrc rename to express-pg-auth0/.nvmrc diff --git a/assets/express/example-app-auth0/.openapi/gitignorefile b/express-pg-auth0/.openapi/.gitignore similarity index 100% rename from assets/express/example-app-auth0/.openapi/gitignorefile rename to express-pg-auth0/.openapi/.gitignore diff --git a/assets/.prettierignore b/express-pg-auth0/.prettierignore similarity index 100% rename from assets/.prettierignore rename to express-pg-auth0/.prettierignore diff --git a/.prettierrc.js b/express-pg-auth0/.prettierrc.js similarity index 100% rename from .prettierrc.js rename to express-pg-auth0/.prettierrc.js diff --git a/express-pg-auth0/.secrets.baseline b/express-pg-auth0/.secrets.baseline new file mode 100644 index 00000000..0c8df8a9 --- /dev/null +++ b/express-pg-auth0/.secrets.baseline @@ -0,0 +1,116 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-20T21:29:38Z" +} diff --git a/assets/express/example-app-auth0/Dockerfile b/express-pg-auth0/Dockerfile similarity index 100% rename from assets/express/example-app-auth0/Dockerfile rename to express-pg-auth0/Dockerfile diff --git a/express-pg-auth0/README.md b/express-pg-auth0/README.md new file mode 100644 index 00000000..4ab0194d --- /dev/null +++ b/express-pg-auth0/README.md @@ -0,0 +1,330 @@ +# express-pg-auth0 + +## Setup + +### Prerequisites + +#### Node and NPM via NVM + +**Install NVM:** + +Follow the instructions [here](https://github.com/nvm-sh/nvm#installing-and-updating). + +**Install the version of node specified in the `.nvmrc` file and switch to it:** + +```bash +nvm install +nvm use +``` + +**(Optional) Set the node version as default:** + +```bash +nvm alias default $(cat .nvmrc) +``` + +**(Optional) Automatically switch Node version when changing directories:** + +Follow the instructions for your shell [here](https://github.com/nvm-sh/nvm#deeper-shell-integration). + +#### Docker + +Follow the instructions [here](https://docs.docker.com/engine/install/). + +**Post installation steps for Linux users:** + +Follow the instructions [here](https://docs.docker.com/engine/install/linux-postinstall/). + +**Docker Compose:** + +In case `docker compose` isn't setup with the docker installation follow the +instructions [here](https://docs.docker.com/compose/install/). + +### Clone the repo and cd into the folder + +```bash +git clone git@github.com:.git +cd +``` + +### Install Node modules + +```bash +npm install +``` + +### Setup environment variables + +Copy the `.env.example` file to `.env`. Update environment variables as needed. + +```bash +cp .env.example .env +``` + +### Setup environment variables for e2e tests + +Copy the `.env` file to `.env.test`. Update environment variables as needed. + +```bash +cp .env .env.test +``` + +Update `NODE_ENV` to `test`. + +Make sure to provide a different database name from what you have in `.env` +as e2e tests wipe the database clean before they run. + +You may want to disable query logging by removing `DEBUG=knex:query`. + +### Setup docker-compose overrides + +Create a `docker-compose.override.yml` file using the example: + +```bash +cp docker-compose.override.example.yml docker-compose.override.yml +``` + +You can open the `docker-compose.override.yml` and adjust the port Swagger UI +is being served on (default is 3001). + +If you're on Apple M1, uncomment the `platform: linux/amd64` line: + +```yaml + swaggerui: + # uncomment the line below for Apple M1 + platform: linux/amd64 + ports: + - '3001:8080' +``` + + +### Provision the services + +In order to provision the services run: + +```bash +docker-compose up -d +``` + +If you want to provision Swagger UI as well, then use the `tools` profile: + +```bash +docker-compose --profile tools up -d +``` + +To learn more about using profiles, checkout the [documentation](https://docs.docker.com/compose/profiles/). + + +### Run database migrations + +```bash +npm run db:migrate:latest +``` + +## Running the app + +```bash +# build the app +npm run build +# run in development mode +npm run start + +# run in watch mode +npm run start:dev + +# run in debug mode +npm run start:debug +``` + +## Test + +### Unit Tests + +```bash +# run +npm run test + +# watch +npm run test:watch + +# coverage +npm run test:cov +``` + +### End-to-end Tests + +```bash +# e2e tests +npm run test:e2e + +# e2e coverage +npm run test:e2e:cov +``` + +## Working with migrations + +The underlying library is `knex`. You can find their guide on migrations [here](http://knexjs.org/guide/migrations.html). + +```bash +# check what is the last applied migration +npm run db:migrate:version + +# check which migrations have been run and how many are pending +npm run db:migrate:status + +# create a new migration file +npm run db:migrate:make + +# run the next migration that has not yet been run +npm run db:migrate:up + +# undo the last migration that was run +npm run db:migrate:down + +# run all pending migrations +npm run db:migrate:latest + +# rollback the last batch of migrations +npm run db:migrate:rollback + +# rollback all migrations +npm run db:migrate:rollback --all + +# rollback all migrations and re-apply latest +npm run db:migrate:reset +``` + +To debug migrations set the `DEBUG` environment variable to `knex:query`, e.g. + +```bash +DEBUG=knex:query npm run db:migrate:up +``` + +## Working with Docker + +```bash +# build the image +npm run image:build + +# run the image +npm run image:run +``` + +## Working with OpenAPI + +In order to generate the OpenAPI document run: + +```bash +npm run openapi:g +``` + +Then, if you haven't already, start the Swagger UI service: + +```bash +docker compose --profile tools up -d +``` + +Otherwise, restart the services: + +```bash +docker compose --profile tools restart +``` + +You only need to do this once, if you ran `docker-compose` before `npm run openapi:g`. + +Now navigate to `http://localhost:3001` and you should be able to see the `To-Do` +API documentation. + +If you're experiencing issues setting this up, checkout the troubleshooting +section at the bottom. + + +## Working with Licenses + +Whenever a new `npm` module is added to the project we need to check its +license. If the license is included in the +[OSI approved license list](https://opensource.org/licenses/), then we +can use it. If not - we need to review it manually. Start by running +the following `npm` script: + +```bash +npm run license:check +``` + +If the script succeeds, that means that the license is either a included +in the OSI list, or it was already reviewed and passed the review. When +the script fails it will print the first license that needs a review. +In order to list all licenses that need a review run the following: + +```bash +npm run license:for-review +``` + +In order to verify that a license suits your case, you can use a website like +[SOOS](https://app.soos.io/research/licenses). Find the license and inspect +the `Usage` and `Requirements` sections. You need to verify that the license +is `permissive`. Look for things like whether the license allows for +`Commercial Use` and `Private Use`. + +If the package passes the review, then include it in the list in +`licenses-reviewed.js` located in the root of ths project. + +If the package does not pass the review, then look for an alternative package. + +Lastly, if you just want to print a summary of all license, run: + +```bash +npm run license:summary +``` + +## Debug + +### VS Code + +Go to the Debug menu (CTRL+SHIFT+D). From `RUN AND DEBUG` at the top select +`Run Script: Launch via NPM`. You should now be able to start debugging +by pressing `F5`. + +## Troubleshooting +### OpenAPI + +If `docker-compose` fails with this message and you're on Apple M1: + +```bash +The requested image's platform (linux/amd64) does not match the detected host +platform (linux/arm64/v8) and no specific platform was requested +``` + +then uncomment the `platform: linux/amd64` line in `docker-compose.override.yml` + +```yaml + swaggerui: + # uncomment the line below for Apple M1 + platform: linux/amd64 + ports: + - '3001:8080' +``` + +If you're seeing `This site can't be reached` after navigating to `http://localhost:3001`, +then it means that the `swaggerui` service is not running. Try provisioning it: + +```bash +docker compose --profile tools up -d +``` + +In case you're seeing the `Swagger Petstore` documentation + +```bash +# generate the OpenApi document +npm run openapi:g + +# restart the services +docker compose --profile tools restart +``` + +If Swagger UI is running successfully, but all requests you make through it are failing, +then you probably forgot to start the application: + +```bash +npm run start:dev +``` diff --git a/assets/express/example-app-auth0/__mocks__/knex.ts b/express-pg-auth0/__mocks__/knex.ts similarity index 100% rename from assets/express/example-app-auth0/__mocks__/knex.ts rename to express-pg-auth0/__mocks__/knex.ts diff --git a/assets/express/example-app-auth0/docker-compose.override.example.yml b/express-pg-auth0/docker-compose.override.example.yml similarity index 100% rename from assets/express/example-app-auth0/docker-compose.override.example.yml rename to express-pg-auth0/docker-compose.override.example.yml diff --git a/assets/express/example-app-auth0/docker-compose.yml b/express-pg-auth0/docker-compose.yml similarity index 100% rename from assets/express/example-app-auth0/docker-compose.yml rename to express-pg-auth0/docker-compose.yml diff --git a/assets/express/example-app-auth0/jest.config.js b/express-pg-auth0/jest.config.js similarity index 100% rename from assets/express/example-app-auth0/jest.config.js rename to express-pg-auth0/jest.config.js diff --git a/express-pg-auth0/jest.setup.ts b/express-pg-auth0/jest.setup.ts new file mode 100644 index 00000000..c86607a2 --- /dev/null +++ b/express-pg-auth0/jest.setup.ts @@ -0,0 +1,2 @@ +import '@extensions/zod/register'; +import '@extensions/knex/register'; \ No newline at end of file diff --git a/assets/licenses/licenses-allowed.js b/express-pg-auth0/licenses-allowed.js similarity index 100% rename from assets/licenses/licenses-allowed.js rename to express-pg-auth0/licenses-allowed.js diff --git a/assets/licenses/licenses-reviewed.js b/express-pg-auth0/licenses-reviewed.js similarity index 100% rename from assets/licenses/licenses-reviewed.js rename to express-pg-auth0/licenses-reviewed.js diff --git a/assets/express/example-app-auth0/migrations/20230302155244_initial.ts b/express-pg-auth0/migrations/20230302155244_initial.ts similarity index 100% rename from assets/express/example-app-auth0/migrations/20230302155244_initial.ts rename to express-pg-auth0/migrations/20230302155244_initial.ts diff --git a/assets/express/example-app-auth0/migrations/20230302162022_create_table_users.ts b/express-pg-auth0/migrations/20230302162022_create_table_users.ts similarity index 100% rename from assets/express/example-app-auth0/migrations/20230302162022_create_table_users.ts rename to express-pg-auth0/migrations/20230302162022_create_table_users.ts diff --git a/assets/express/example-app-auth0/migrations/20230302162023_create_table_todos.ts b/express-pg-auth0/migrations/20230302162023_create_table_todos.ts similarity index 100% rename from assets/express/example-app-auth0/migrations/20230302162023_create_table_todos.ts rename to express-pg-auth0/migrations/20230302162023_create_table_todos.ts diff --git a/assets/express/example-app-auth0/migrations/utils/index.ts b/express-pg-auth0/migrations/utils/index.ts similarity index 100% rename from assets/express/example-app-auth0/migrations/utils/index.ts rename to express-pg-auth0/migrations/utils/index.ts diff --git a/express-pg-auth0/nodemon.json b/express-pg-auth0/nodemon.json new file mode 100644 index 00000000..aa3eb0d1 --- /dev/null +++ b/express-pg-auth0/nodemon.json @@ -0,0 +1,12 @@ +{ + "watch": [ + "src" + ], + "ext": "ts", + "ignore": [ + "**/*.spec.*", + "**/*.test.*" + ], + "signal": "SIGINT", + "exec": "npx ts-node src/index" +} diff --git a/express-pg-auth0/package.json b/express-pg-auth0/package.json new file mode 100644 index 00000000..cac9d352 --- /dev/null +++ b/express-pg-auth0/package.json @@ -0,0 +1,113 @@ +{ + "name": "express-pg-auth0", + "version": "1.0.0", + "private": true, + "description": "", + "keywords": [], + "license": "ISC", + "author": "", + "main": "index.js", + "scripts": { + "build": "npm run clean && tsc -p tsconfig.build.json && tsc-alias", + "clean": "rimraf dist", + "db:connection:print": "ts-node scripts/db-connection", + "db:migrate:down": "ts-node node_modules/.bin/knex migrate:down --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:latest": "ts-node node_modules/.bin/knex migrate:latest --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:make": "knex migrate:make -x ts --migrations-directory ./migrations", + "db:migrate:reset": "npm run db:migrate:rollback --all && npm run db:migrate:latest", + "db:migrate:rollback": "ts-node node_modules/.bin/knex migrate:rollback --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:status": "ts-node node_modules/.bin/knex migrate:status --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:up": "ts-node node_modules/.bin/knex migrate:up --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:version": "ts-node node_modules/.bin/knex migrate:currentVersion --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "format": "prettier \"**/*.{js,cjs,mjs,ts,md}\" --write --cache --cache-strategy metadata --cache-location .prettiercache", + "image:build": "DOCKER_BUILDKIT=1 docker build -t express-pg-auth0 .", + "image:run": "docker run --rm --net host -e NODE_ENV=production --env-file .env express-pg-auth0", + "license:check": "license-checker --summary --excludePrivatePackages --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --excludePrivatePackages --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint . --ext .js,.cjs,.mjs,.ts --fix --cache", + "lint:dockerfile": "docker run --rm -i hadolint/hadolint < Dockerfile", + "lint:markdown": "markdownlint **/*.md --ignore node_modules", + "openapi:g": "ts-node scripts/generate-openapi", + "prepare": "husky install", + "start": "npx ts-node src/index", + "start:debug": "tsc --sourceMap -p tsconfig.build.json && tsc-alias && node --inspect -r dotenv/config dist/index", + "start:dev": "nodemon", + "test": "jest", + "test:cov": "jest --coverage", + "test:e2e": "npx jest --config ./test/jest-e2e.config.js --runInBand", + "test:e2e:cov": "npm run test:e2e -- --coverage" + }, + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "axios": "^1.6.3", + "bcrypt": "^5.1.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "express-jwt": "^8.4.1", + "express-oauth2-jwt-bearer": "^1.6.0", + "helmet": "^6.0.1", + "http-errors": "^2.0.0", + "http-terminator": "^3.2.0", + "jsonwebtoken": "^9.0.0", + "knex": "^2.4.2", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "pino": "^8.11.0", + "query-types": "^0.1.4", + "statuses": "^2.0.1", + "zod": "^3.20.6", + "zod-openapi": "~2.2.2" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@ls-lint/ls-lint": "^1.11.2", + "@tsconfig/recommended": "^1.0.2", + "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.2", + "@types/cors": "^2.8.5", + "@types/express": "^4.17.17", + "@types/http-errors": "^2.0.1", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "^18.14.0", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-security": "~1.7.1", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "nodemon": "^3.0.2", + "pgtools": "^1.0.0", + "pino-pretty": "^9.4.0", + "prettier": "^2.8.4", + "rimraf": "^4.1.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "tsc-alias": "^1.8.2", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" + } +} \ No newline at end of file diff --git a/assets/db/pg/scripts/db-connection.ts b/express-pg-auth0/scripts/db-connection.ts similarity index 100% rename from assets/db/pg/scripts/db-connection.ts rename to express-pg-auth0/scripts/db-connection.ts diff --git a/assets/detect-secrets.sh b/express-pg-auth0/scripts/detect-secrets.sh similarity index 100% rename from assets/detect-secrets.sh rename to express-pg-auth0/scripts/detect-secrets.sh diff --git a/assets/express/example-app-auth0/scripts/generate-openapi.ts b/express-pg-auth0/scripts/generate-openapi.ts similarity index 100% rename from assets/express/example-app-auth0/scripts/generate-openapi.ts rename to express-pg-auth0/scripts/generate-openapi.ts diff --git a/assets/express/example-app-auth0/src/@types/express/index.d.ts b/express-pg-auth0/src/@types/express/index.d.ts similarity index 100% rename from assets/express/example-app-auth0/src/@types/express/index.d.ts rename to express-pg-auth0/src/@types/express/index.d.ts diff --git a/assets/express/example-app-auth0/src/@types/knex/index.d.ts b/express-pg-auth0/src/@types/knex/index.d.ts similarity index 100% rename from assets/express/example-app-auth0/src/@types/knex/index.d.ts rename to express-pg-auth0/src/@types/knex/index.d.ts diff --git a/assets/express/example-app-auth0/src/@types/query-types/index.d.ts b/express-pg-auth0/src/@types/query-types/index.d.ts similarity index 100% rename from assets/express/example-app-auth0/src/@types/query-types/index.d.ts rename to express-pg-auth0/src/@types/query-types/index.d.ts diff --git a/assets/express/example-app-auth0/src/api/auth/guards/auth-guard.ts b/express-pg-auth0/src/api/auth/guards/auth-guard.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/guards/auth-guard.ts rename to express-pg-auth0/src/api/auth/guards/auth-guard.ts diff --git a/assets/express/example-app-auth0/src/api/auth/guards/index.ts b/express-pg-auth0/src/api/auth/guards/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/guards/index.ts rename to express-pg-auth0/src/api/auth/guards/index.ts diff --git a/assets/express/example-app-auth0/src/api/auth/index.ts b/express-pg-auth0/src/api/auth/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/index.ts rename to express-pg-auth0/src/api/auth/index.ts diff --git a/assets/express/example-app-auth0/src/api/auth/interfaces/auth.ts b/express-pg-auth0/src/api/auth/interfaces/auth.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/interfaces/auth.ts rename to express-pg-auth0/src/api/auth/interfaces/auth.ts diff --git a/assets/express/example-app-auth0/src/api/auth/interfaces/index.ts b/express-pg-auth0/src/api/auth/interfaces/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/interfaces/index.ts rename to express-pg-auth0/src/api/auth/interfaces/index.ts diff --git a/assets/express/example-app-auth0/src/api/auth/routes/index.ts b/express-pg-auth0/src/api/auth/routes/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/routes/index.ts rename to express-pg-auth0/src/api/auth/routes/index.ts diff --git a/assets/express/example-app-auth0/src/api/auth/routes/register.route.ts b/express-pg-auth0/src/api/auth/routes/register.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/routes/register.route.ts rename to express-pg-auth0/src/api/auth/routes/register.route.ts diff --git a/assets/express/example-app-auth0/src/api/auth/schemas/credentials.schema.ts b/express-pg-auth0/src/api/auth/schemas/credentials.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/schemas/credentials.schema.ts rename to express-pg-auth0/src/api/auth/schemas/credentials.schema.ts diff --git a/assets/express/example-app-auth0/src/api/auth/schemas/index.ts b/express-pg-auth0/src/api/auth/schemas/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/schemas/index.ts rename to express-pg-auth0/src/api/auth/schemas/index.ts diff --git a/assets/express/example-app-auth0/src/api/auth/schemas/user.schema.ts b/express-pg-auth0/src/api/auth/schemas/user.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/schemas/user.schema.ts rename to express-pg-auth0/src/api/auth/schemas/user.schema.ts diff --git a/assets/express/example-app-auth0/src/api/auth/services/auth.service.ts b/express-pg-auth0/src/api/auth/services/auth.service.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/services/auth.service.ts rename to express-pg-auth0/src/api/auth/services/auth.service.ts diff --git a/assets/express/example-app-auth0/src/api/auth/services/auth0.service.ts b/express-pg-auth0/src/api/auth/services/auth0.service.ts similarity index 98% rename from assets/express/example-app-auth0/src/api/auth/services/auth0.service.ts rename to express-pg-auth0/src/api/auth/services/auth0.service.ts index fbc14d64..2efb658d 100644 --- a/assets/express/example-app-auth0/src/api/auth/services/auth0.service.ts +++ b/express-pg-auth0/src/api/auth/services/auth0.service.ts @@ -1,4 +1,4 @@ -import { Axios } from 'axios'; +import { AxiosStatic } from 'axios'; import { Logger } from 'pino'; import { Auth0User } from '../interfaces'; import { Environment } from '@utils/environment'; @@ -12,7 +12,7 @@ export class Auth0Service { constructor( private logger: Logger, - private axios: Axios, + private axios: AxiosStatic, private env: Environment, ) { this.getAuth0AccessToken() diff --git a/assets/express/example-app-auth0/src/api/auth/services/index.ts b/express-pg-auth0/src/api/auth/services/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/auth/services/index.ts rename to express-pg-auth0/src/api/auth/services/index.ts diff --git a/assets/express/example-app-auth0/src/api/healthchecks/index.ts b/express-pg-auth0/src/api/healthchecks/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/healthchecks/index.ts rename to express-pg-auth0/src/api/healthchecks/index.ts diff --git a/assets/express/example-app-auth0/src/api/healthchecks/routes/index.ts b/express-pg-auth0/src/api/healthchecks/routes/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/healthchecks/routes/index.ts rename to express-pg-auth0/src/api/healthchecks/routes/index.ts diff --git a/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.test.ts b/express-pg-auth0/src/api/healthchecks/routes/live.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.test.ts rename to express-pg-auth0/src/api/healthchecks/routes/live.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.ts b/express-pg-auth0/src/api/healthchecks/routes/live.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.ts rename to express-pg-auth0/src/api/healthchecks/routes/live.route.ts diff --git a/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.test.ts b/express-pg-auth0/src/api/healthchecks/routes/ready.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.test.ts rename to express-pg-auth0/src/api/healthchecks/routes/ready.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.ts b/express-pg-auth0/src/api/healthchecks/routes/ready.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.ts rename to express-pg-auth0/src/api/healthchecks/routes/ready.route.ts diff --git a/assets/express/example-app-auth0/src/api/todos/entities/index.ts b/express-pg-auth0/src/api/todos/entities/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/entities/index.ts rename to express-pg-auth0/src/api/todos/entities/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/entities/insert-todo.entity.ts b/express-pg-auth0/src/api/todos/entities/insert-todo.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/entities/insert-todo.entity.ts rename to express-pg-auth0/src/api/todos/entities/insert-todo.entity.ts diff --git a/assets/express/example-app-auth0/src/api/todos/entities/list-todos-query.entity.ts b/express-pg-auth0/src/api/todos/entities/list-todos-query.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/entities/list-todos-query.entity.ts rename to express-pg-auth0/src/api/todos/entities/list-todos-query.entity.ts diff --git a/assets/express/example-app-auth0/src/api/todos/entities/todo.entity.ts b/express-pg-auth0/src/api/todos/entities/todo.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/entities/todo.entity.ts rename to express-pg-auth0/src/api/todos/entities/todo.entity.ts diff --git a/assets/express/example-app-auth0/src/api/todos/entities/update-todo.entity.ts b/express-pg-auth0/src/api/todos/entities/update-todo.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/entities/update-todo.entity.ts rename to express-pg-auth0/src/api/todos/entities/update-todo.entity.ts diff --git a/assets/express/example-app-auth0/src/api/todos/error-mappings/index.ts b/express-pg-auth0/src/api/todos/error-mappings/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/error-mappings/index.ts rename to express-pg-auth0/src/api/todos/error-mappings/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/express-pg-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts rename to express-pg-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts diff --git a/assets/express/example-app-auth0/src/api/todos/filters/index.ts b/express-pg-auth0/src/api/todos/filters/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/filters/index.ts rename to express-pg-auth0/src/api/todos/filters/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts b/express-pg-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts rename to express-pg-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.ts b/express-pg-auth0/src/api/todos/filters/todos-by-completed.filter.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.ts rename to express-pg-auth0/src/api/todos/filters/todos-by-completed.filter.ts diff --git a/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.test.ts b/express-pg-auth0/src/api/todos/filters/todos-by-name.filter.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.test.ts rename to express-pg-auth0/src/api/todos/filters/todos-by-name.filter.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.ts b/express-pg-auth0/src/api/todos/filters/todos-by-name.filter.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.ts rename to express-pg-auth0/src/api/todos/filters/todos-by-name.filter.ts diff --git a/assets/express/example-app-auth0/src/api/todos/index.ts b/express-pg-auth0/src/api/todos/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/index.ts rename to express-pg-auth0/src/api/todos/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts b/express-pg-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts rename to express-pg-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts diff --git a/assets/express/example-app-auth0/src/api/todos/repositories/index.ts b/express-pg-auth0/src/api/todos/repositories/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/repositories/index.ts rename to express-pg-auth0/src/api/todos/repositories/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.test.ts b/express-pg-auth0/src/api/todos/repositories/todos.repository.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.test.ts rename to express-pg-auth0/src/api/todos/repositories/todos.repository.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.ts b/express-pg-auth0/src/api/todos/repositories/todos.repository.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.ts rename to express-pg-auth0/src/api/todos/repositories/todos.repository.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/create.route.test.ts b/express-pg-auth0/src/api/todos/routes/create.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/create.route.test.ts rename to express-pg-auth0/src/api/todos/routes/create.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/create.route.ts b/express-pg-auth0/src/api/todos/routes/create.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/create.route.ts rename to express-pg-auth0/src/api/todos/routes/create.route.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/delete.route.test.ts b/express-pg-auth0/src/api/todos/routes/delete.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/delete.route.test.ts rename to express-pg-auth0/src/api/todos/routes/delete.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/delete.route.ts b/express-pg-auth0/src/api/todos/routes/delete.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/delete.route.ts rename to express-pg-auth0/src/api/todos/routes/delete.route.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/get.route.test.ts b/express-pg-auth0/src/api/todos/routes/get.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/get.route.test.ts rename to express-pg-auth0/src/api/todos/routes/get.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/get.route.ts b/express-pg-auth0/src/api/todos/routes/get.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/get.route.ts rename to express-pg-auth0/src/api/todos/routes/get.route.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/index.ts b/express-pg-auth0/src/api/todos/routes/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/index.ts rename to express-pg-auth0/src/api/todos/routes/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/list.route.test.ts b/express-pg-auth0/src/api/todos/routes/list.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/list.route.test.ts rename to express-pg-auth0/src/api/todos/routes/list.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/list.route.ts b/express-pg-auth0/src/api/todos/routes/list.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/list.route.ts rename to express-pg-auth0/src/api/todos/routes/list.route.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/update.route.test.ts b/express-pg-auth0/src/api/todos/routes/update.route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/update.route.test.ts rename to express-pg-auth0/src/api/todos/routes/update.route.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/routes/update.route.ts b/express-pg-auth0/src/api/todos/routes/update.route.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/routes/update.route.ts rename to express-pg-auth0/src/api/todos/routes/update.route.ts diff --git a/assets/express/example-app-auth0/src/api/todos/schemas/create-todo.schema.ts b/express-pg-auth0/src/api/todos/schemas/create-todo.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/schemas/create-todo.schema.ts rename to express-pg-auth0/src/api/todos/schemas/create-todo.schema.ts diff --git a/assets/express/example-app-auth0/src/api/todos/schemas/index.ts b/express-pg-auth0/src/api/todos/schemas/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/schemas/index.ts rename to express-pg-auth0/src/api/todos/schemas/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/schemas/list-todos-query.schema.ts b/express-pg-auth0/src/api/todos/schemas/list-todos-query.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/schemas/list-todos-query.schema.ts rename to express-pg-auth0/src/api/todos/schemas/list-todos-query.schema.ts diff --git a/assets/express/example-app-auth0/src/api/todos/schemas/todo-id-param.schema.ts b/express-pg-auth0/src/api/todos/schemas/todo-id-param.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/schemas/todo-id-param.schema.ts rename to express-pg-auth0/src/api/todos/schemas/todo-id-param.schema.ts diff --git a/assets/express/example-app-auth0/src/api/todos/schemas/todo.schema.ts b/express-pg-auth0/src/api/todos/schemas/todo.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/schemas/todo.schema.ts rename to express-pg-auth0/src/api/todos/schemas/todo.schema.ts diff --git a/assets/express/example-app-auth0/src/api/todos/schemas/update-todo.schema.ts b/express-pg-auth0/src/api/todos/schemas/update-todo.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/schemas/update-todo.schema.ts rename to express-pg-auth0/src/api/todos/schemas/update-todo.schema.ts diff --git a/assets/express/example-app-auth0/src/api/todos/services/index.ts b/express-pg-auth0/src/api/todos/services/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/services/index.ts rename to express-pg-auth0/src/api/todos/services/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/services/todos.service.test.ts b/express-pg-auth0/src/api/todos/services/todos.service.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/services/todos.service.test.ts rename to express-pg-auth0/src/api/todos/services/todos.service.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/services/todos.service.ts b/express-pg-auth0/src/api/todos/services/todos.service.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/services/todos.service.ts rename to express-pg-auth0/src/api/todos/services/todos.service.ts diff --git a/assets/express/example-app-auth0/src/api/todos/sorters/index.ts b/express-pg-auth0/src/api/todos/sorters/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/sorters/index.ts rename to express-pg-auth0/src/api/todos/sorters/index.ts diff --git a/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts b/express-pg-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts rename to express-pg-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts b/express-pg-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts rename to express-pg-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts diff --git a/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts b/express-pg-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts rename to express-pg-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts diff --git a/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.ts b/express-pg-auth0/src/api/todos/sorters/todos-by-name.sorter.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.ts rename to express-pg-auth0/src/api/todos/sorters/todos-by-name.sorter.ts diff --git a/assets/express/example-app-auth0/src/api/users/entities/index.ts b/express-pg-auth0/src/api/users/entities/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/entities/index.ts rename to express-pg-auth0/src/api/users/entities/index.ts diff --git a/assets/express/example-app-auth0/src/api/users/entities/insert-user.entity.ts b/express-pg-auth0/src/api/users/entities/insert-user.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/entities/insert-user.entity.ts rename to express-pg-auth0/src/api/users/entities/insert-user.entity.ts diff --git a/assets/express/example-app-auth0/src/api/users/entities/update-user.entity.ts b/express-pg-auth0/src/api/users/entities/update-user.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/entities/update-user.entity.ts rename to express-pg-auth0/src/api/users/entities/update-user.entity.ts diff --git a/assets/express/example-app-auth0/src/api/users/entities/user.entity.ts b/express-pg-auth0/src/api/users/entities/user.entity.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/entities/user.entity.ts rename to express-pg-auth0/src/api/users/entities/user.entity.ts diff --git a/assets/express/example-app-auth0/src/api/users/error-mappings/index.ts b/express-pg-auth0/src/api/users/error-mappings/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/error-mappings/index.ts rename to express-pg-auth0/src/api/users/error-mappings/index.ts diff --git a/assets/express/example-app-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/express-pg-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts rename to express-pg-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts diff --git a/assets/express/example-app-auth0/src/api/users/index.ts b/express-pg-auth0/src/api/users/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/index.ts rename to express-pg-auth0/src/api/users/index.ts diff --git a/assets/express/example-app-auth0/src/api/users/repositories/__mocks__/users.repository.ts b/express-pg-auth0/src/api/users/repositories/__mocks__/users.repository.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/repositories/__mocks__/users.repository.ts rename to express-pg-auth0/src/api/users/repositories/__mocks__/users.repository.ts diff --git a/assets/express/example-app-auth0/src/api/users/repositories/index.ts b/express-pg-auth0/src/api/users/repositories/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/repositories/index.ts rename to express-pg-auth0/src/api/users/repositories/index.ts diff --git a/assets/express/example-app-auth0/src/api/users/repositories/users.repository.test.ts b/express-pg-auth0/src/api/users/repositories/users.repository.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/repositories/users.repository.test.ts rename to express-pg-auth0/src/api/users/repositories/users.repository.test.ts diff --git a/assets/express/example-app-auth0/src/api/users/repositories/users.repository.ts b/express-pg-auth0/src/api/users/repositories/users.repository.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/repositories/users.repository.ts rename to express-pg-auth0/src/api/users/repositories/users.repository.ts diff --git a/assets/express/example-app-auth0/src/api/users/schemas/index.ts b/express-pg-auth0/src/api/users/schemas/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/schemas/index.ts rename to express-pg-auth0/src/api/users/schemas/index.ts diff --git a/assets/express/example-app-auth0/src/api/users/schemas/user.schema.ts b/express-pg-auth0/src/api/users/schemas/user.schema.ts similarity index 100% rename from assets/express/example-app-auth0/src/api/users/schemas/user.schema.ts rename to express-pg-auth0/src/api/users/schemas/user.schema.ts diff --git a/assets/express/example-app-auth0/src/app.test.ts b/express-pg-auth0/src/app.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/app.test.ts rename to express-pg-auth0/src/app.test.ts diff --git a/assets/express/example-app-auth0/src/app.ts b/express-pg-auth0/src/app.ts similarity index 100% rename from assets/express/example-app-auth0/src/app.ts rename to express-pg-auth0/src/app.ts diff --git a/assets/express/example-app-auth0/src/database/client.test.ts b/express-pg-auth0/src/database/client.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/client.test.ts rename to express-pg-auth0/src/database/client.test.ts diff --git a/assets/express/example-app-auth0/src/database/client.ts b/express-pg-auth0/src/database/client.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/client.ts rename to express-pg-auth0/src/database/client.ts diff --git a/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts b/express-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts rename to express-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts diff --git a/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/express-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts rename to express-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts diff --git a/assets/express/example-app-auth0/src/database/error-mappings/index.ts b/express-pg-auth0/src/database/error-mappings/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/error-mappings/index.ts rename to express-pg-auth0/src/database/error-mappings/index.ts diff --git a/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts b/express-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts rename to express-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts diff --git a/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.ts b/express-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.ts rename to express-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.ts diff --git a/assets/express/example-app-auth0/src/database/errors/duplicate-record.error.ts b/express-pg-auth0/src/database/errors/duplicate-record.error.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/errors/duplicate-record.error.ts rename to express-pg-auth0/src/database/errors/duplicate-record.error.ts diff --git a/assets/express/example-app-auth0/src/database/errors/index.ts b/express-pg-auth0/src/database/errors/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/errors/index.ts rename to express-pg-auth0/src/database/errors/index.ts diff --git a/assets/express/example-app-auth0/src/database/errors/record-not-found.error.ts b/express-pg-auth0/src/database/errors/record-not-found.error.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/errors/record-not-found.error.ts rename to express-pg-auth0/src/database/errors/record-not-found.error.ts diff --git a/assets/express/example-app-auth0/src/database/index.ts b/express-pg-auth0/src/database/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/index.ts rename to express-pg-auth0/src/database/index.ts diff --git a/assets/express/example-app-auth0/src/database/on-init.ts b/express-pg-auth0/src/database/on-init.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/on-init.ts rename to express-pg-auth0/src/database/on-init.ts diff --git a/assets/express/example-app-auth0/src/database/operations.ts b/express-pg-auth0/src/database/operations.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/operations.ts rename to express-pg-auth0/src/database/operations.ts diff --git a/assets/express/example-app-auth0/src/database/query-builder/extensions.test.ts b/express-pg-auth0/src/database/query-builder/extensions.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/query-builder/extensions.test.ts rename to express-pg-auth0/src/database/query-builder/extensions.test.ts diff --git a/assets/express/example-app-auth0/src/database/query-builder/extensions.ts b/express-pg-auth0/src/database/query-builder/extensions.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/query-builder/extensions.ts rename to express-pg-auth0/src/database/query-builder/extensions.ts diff --git a/assets/express/example-app-auth0/src/database/query-builder/index.ts b/express-pg-auth0/src/database/query-builder/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/query-builder/index.ts rename to express-pg-auth0/src/database/query-builder/index.ts diff --git a/assets/express/example-app-auth0/src/database/seeds/test/index.ts b/express-pg-auth0/src/database/seeds/test/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/seeds/test/index.ts rename to express-pg-auth0/src/database/seeds/test/index.ts diff --git a/assets/express/example-app-auth0/src/database/utils/error.ts b/express-pg-auth0/src/database/utils/error.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/utils/error.ts rename to express-pg-auth0/src/database/utils/error.ts diff --git a/assets/express/example-app-auth0/src/database/utils/index.ts b/express-pg-auth0/src/database/utils/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/database/utils/index.ts rename to express-pg-auth0/src/database/utils/index.ts diff --git a/assets/express/example-app-auth0/src/extensions/knex/index.ts b/express-pg-auth0/src/extensions/knex/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/extensions/knex/index.ts rename to express-pg-auth0/src/extensions/knex/index.ts diff --git a/assets/express/example-app-auth0/src/extensions/knex/register.ts b/express-pg-auth0/src/extensions/knex/register.ts similarity index 100% rename from assets/express/example-app-auth0/src/extensions/knex/register.ts rename to express-pg-auth0/src/extensions/knex/register.ts diff --git a/assets/express/example-app-auth0/src/extensions/zod/index.ts b/express-pg-auth0/src/extensions/zod/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/extensions/zod/index.ts rename to express-pg-auth0/src/extensions/zod/index.ts diff --git a/assets/express/example-app-auth0/src/extensions/zod/register.ts b/express-pg-auth0/src/extensions/zod/register.ts similarity index 100% rename from assets/express/example-app-auth0/src/extensions/zod/register.ts rename to express-pg-auth0/src/extensions/zod/register.ts diff --git a/assets/express/example-app-auth0/src/index.ts b/express-pg-auth0/src/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/index.ts rename to express-pg-auth0/src/index.ts diff --git a/assets/express/example-app-auth0/src/middleware/attach-services.middleware.test.ts b/express-pg-auth0/src/middleware/attach-services.middleware.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/attach-services.middleware.test.ts rename to express-pg-auth0/src/middleware/attach-services.middleware.test.ts diff --git a/assets/express/example-app-auth0/src/middleware/attach-services.middleware.ts b/express-pg-auth0/src/middleware/attach-services.middleware.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/attach-services.middleware.ts rename to express-pg-auth0/src/middleware/attach-services.middleware.ts diff --git a/assets/express/example-app-auth0/src/middleware/handle-error.middleware.test.ts b/express-pg-auth0/src/middleware/handle-error.middleware.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/handle-error.middleware.test.ts rename to express-pg-auth0/src/middleware/handle-error.middleware.test.ts diff --git a/assets/express/example-app-auth0/src/middleware/handle-error.middleware.ts b/express-pg-auth0/src/middleware/handle-error.middleware.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/handle-error.middleware.ts rename to express-pg-auth0/src/middleware/handle-error.middleware.ts diff --git a/assets/express/example-app-auth0/src/middleware/index.ts b/express-pg-auth0/src/middleware/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/index.ts rename to express-pg-auth0/src/middleware/index.ts diff --git a/assets/express/example-app-auth0/src/middleware/log-request.middleware.test.ts b/express-pg-auth0/src/middleware/log-request.middleware.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/log-request.middleware.test.ts rename to express-pg-auth0/src/middleware/log-request.middleware.test.ts diff --git a/assets/express/example-app-auth0/src/middleware/log-request.middleware.ts b/express-pg-auth0/src/middleware/log-request.middleware.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/log-request.middleware.ts rename to express-pg-auth0/src/middleware/log-request.middleware.ts diff --git a/assets/express/example-app-auth0/src/middleware/map-error.middleware.test.ts b/express-pg-auth0/src/middleware/map-error.middleware.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/map-error.middleware.test.ts rename to express-pg-auth0/src/middleware/map-error.middleware.test.ts diff --git a/assets/express/example-app-auth0/src/middleware/map-error.middleware.ts b/express-pg-auth0/src/middleware/map-error.middleware.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/map-error.middleware.ts rename to express-pg-auth0/src/middleware/map-error.middleware.ts diff --git a/assets/express/example-app-auth0/src/middleware/validate-request.middleware.test.ts b/express-pg-auth0/src/middleware/validate-request.middleware.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/validate-request.middleware.test.ts rename to express-pg-auth0/src/middleware/validate-request.middleware.test.ts diff --git a/assets/express/example-app-auth0/src/middleware/validate-request.middleware.ts b/express-pg-auth0/src/middleware/validate-request.middleware.ts similarity index 100% rename from assets/express/example-app-auth0/src/middleware/validate-request.middleware.ts rename to express-pg-auth0/src/middleware/validate-request.middleware.ts diff --git a/assets/express/example-app-auth0/src/utils/api/handler.test.ts b/express-pg-auth0/src/utils/api/handler.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/handler.test.ts rename to express-pg-auth0/src/utils/api/handler.test.ts diff --git a/assets/express/example-app-auth0/src/utils/api/handler.ts b/express-pg-auth0/src/utils/api/handler.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/handler.ts rename to express-pg-auth0/src/utils/api/handler.ts diff --git a/assets/express/example-app-auth0/src/utils/api/index.ts b/express-pg-auth0/src/utils/api/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/index.ts rename to express-pg-auth0/src/utils/api/index.ts diff --git a/assets/express/example-app-auth0/src/utils/api/response.test.ts b/express-pg-auth0/src/utils/api/response.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/response.test.ts rename to express-pg-auth0/src/utils/api/response.test.ts diff --git a/assets/express/example-app-auth0/src/utils/api/response.ts b/express-pg-auth0/src/utils/api/response.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/response.ts rename to express-pg-auth0/src/utils/api/response.ts diff --git a/assets/express/example-app-auth0/src/utils/api/route.test.ts b/express-pg-auth0/src/utils/api/route.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/route.test.ts rename to express-pg-auth0/src/utils/api/route.test.ts diff --git a/assets/express/example-app-auth0/src/utils/api/route.ts b/express-pg-auth0/src/utils/api/route.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/api/route.ts rename to express-pg-auth0/src/utils/api/route.ts diff --git a/assets/express/example-app-auth0/src/utils/environment.test.ts b/express-pg-auth0/src/utils/environment.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/environment.test.ts rename to express-pg-auth0/src/utils/environment.test.ts diff --git a/assets/express/example-app-auth0/src/utils/environment.ts b/express-pg-auth0/src/utils/environment.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/environment.ts rename to express-pg-auth0/src/utils/environment.ts diff --git a/assets/express/example-app-auth0/src/utils/error.test.ts b/express-pg-auth0/src/utils/error.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/error.test.ts rename to express-pg-auth0/src/utils/error.test.ts diff --git a/assets/express/example-app-auth0/src/utils/error.ts b/express-pg-auth0/src/utils/error.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/error.ts rename to express-pg-auth0/src/utils/error.ts diff --git a/assets/express/example-app-auth0/src/utils/openapi.test.ts b/express-pg-auth0/src/utils/openapi.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/openapi.test.ts rename to express-pg-auth0/src/utils/openapi.test.ts diff --git a/assets/express/example-app-auth0/src/utils/openapi.ts b/express-pg-auth0/src/utils/openapi.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/openapi.ts rename to express-pg-auth0/src/utils/openapi.ts diff --git a/assets/express/example-app-auth0/src/utils/query/error.test.ts b/express-pg-auth0/src/utils/query/error.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/error.test.ts rename to express-pg-auth0/src/utils/query/error.test.ts diff --git a/assets/express/example-app-auth0/src/utils/query/error.ts b/express-pg-auth0/src/utils/query/error.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/error.ts rename to express-pg-auth0/src/utils/query/error.ts diff --git a/assets/express/example-app-auth0/src/utils/query/filtration.ts b/express-pg-auth0/src/utils/query/filtration.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/filtration.ts rename to express-pg-auth0/src/utils/query/filtration.ts diff --git a/assets/express/example-app-auth0/src/utils/query/index.ts b/express-pg-auth0/src/utils/query/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/index.ts rename to express-pg-auth0/src/utils/query/index.ts diff --git a/assets/express/example-app-auth0/src/utils/query/pagination.test.ts b/express-pg-auth0/src/utils/query/pagination.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/pagination.test.ts rename to express-pg-auth0/src/utils/query/pagination.test.ts diff --git a/assets/express/example-app-auth0/src/utils/query/pagination.ts b/express-pg-auth0/src/utils/query/pagination.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/pagination.ts rename to express-pg-auth0/src/utils/query/pagination.ts diff --git a/assets/express/example-app-auth0/src/utils/query/result.test.ts b/express-pg-auth0/src/utils/query/result.test.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/result.test.ts rename to express-pg-auth0/src/utils/query/result.test.ts diff --git a/assets/express/example-app-auth0/src/utils/query/result.ts b/express-pg-auth0/src/utils/query/result.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/result.ts rename to express-pg-auth0/src/utils/query/result.ts diff --git a/assets/express/example-app-auth0/src/utils/query/sorting.ts b/express-pg-auth0/src/utils/query/sorting.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/query/sorting.ts rename to express-pg-auth0/src/utils/query/sorting.ts diff --git a/assets/express/example-app-auth0/src/utils/types.ts b/express-pg-auth0/src/utils/types.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/types.ts rename to express-pg-auth0/src/utils/types.ts diff --git a/assets/express/example-app-auth0/src/utils/validation/attribute-sets.ts b/express-pg-auth0/src/utils/validation/attribute-sets.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/validation/attribute-sets.ts rename to express-pg-auth0/src/utils/validation/attribute-sets.ts diff --git a/assets/express/example-app-auth0/src/utils/validation/attributes.ts b/express-pg-auth0/src/utils/validation/attributes.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/validation/attributes.ts rename to express-pg-auth0/src/utils/validation/attributes.ts diff --git a/assets/express/example-app-auth0/src/utils/validation/index.ts b/express-pg-auth0/src/utils/validation/index.ts similarity index 100% rename from assets/express/example-app-auth0/src/utils/validation/index.ts rename to express-pg-auth0/src/utils/validation/index.ts diff --git a/assets/express/example-app-auth0/test/healthchecks/live.spec-e2e.ts b/express-pg-auth0/test/healthchecks/live.spec-e2e.ts similarity index 100% rename from assets/express/example-app-auth0/test/healthchecks/live.spec-e2e.ts rename to express-pg-auth0/test/healthchecks/live.spec-e2e.ts diff --git a/assets/express/example-app-auth0/test/healthchecks/ready.spec-e2e.ts b/express-pg-auth0/test/healthchecks/ready.spec-e2e.ts similarity index 100% rename from assets/express/example-app-auth0/test/healthchecks/ready.spec-e2e.ts rename to express-pg-auth0/test/healthchecks/ready.spec-e2e.ts diff --git a/assets/express/example-app-auth0/test/jest-e2e.config.js b/express-pg-auth0/test/jest-e2e.config.js similarity index 100% rename from assets/express/example-app-auth0/test/jest-e2e.config.js rename to express-pg-auth0/test/jest-e2e.config.js diff --git a/express-pg-auth0/test/jest.setup.ts b/express-pg-auth0/test/jest.setup.ts new file mode 100644 index 00000000..e34878f0 --- /dev/null +++ b/express-pg-auth0/test/jest.setup.ts @@ -0,0 +1,7 @@ +import '@extensions/zod/register'; +import '@extensions/knex/register'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/assets/express/example-app-auth0/test/todos/create.spec-e2e.ts b/express-pg-auth0/test/todos/create.spec-e2e.ts similarity index 93% rename from assets/express/example-app-auth0/test/todos/create.spec-e2e.ts rename to express-pg-auth0/test/todos/create.spec-e2e.ts index d7ded83e..a389598b 100644 --- a/assets/express/example-app-auth0/test/todos/create.spec-e2e.ts +++ b/express-pg-auth0/test/todos/create.spec-e2e.ts @@ -41,8 +41,8 @@ describe('POST /v1/todos', () => { authMock.mockImplementation( ( - req: Request & { auth: { payload: { sub: string } } }, - res: Response, + req: { auth: { payload: { sub: string } } }, + res, next: () => true ) => { req.auth = { @@ -83,7 +83,7 @@ describe('POST /v1/todos', () => { }); it('should return 401 error when the user is not authenticated', async () => { - authMock.mockImplementation((request: Request, response: Response, next) => { + authMock.mockImplementation((request, response, next) => { next(createError(401, 'No authorization token was found')) }); diff --git a/assets/express/example-app-auth0/test/todos/delete.spec-e2e.ts b/express-pg-auth0/test/todos/delete.spec-e2e.ts similarity index 92% rename from assets/express/example-app-auth0/test/todos/delete.spec-e2e.ts rename to express-pg-auth0/test/todos/delete.spec-e2e.ts index dbca8c8c..515f44be 100644 --- a/assets/express/example-app-auth0/test/todos/delete.spec-e2e.ts +++ b/express-pg-auth0/test/todos/delete.spec-e2e.ts @@ -41,8 +41,8 @@ describe('DELETE /v1/todos/:id', () => { authMock.mockImplementation( ( - req: Request & { auth: { payload: { sub: string } } }, - res: Response, + req: { auth: { payload: { sub: string } } }, + res, next: () => true ) => { req.auth = { @@ -76,7 +76,7 @@ describe('DELETE /v1/todos/:id', () => { }); it('should return 401 error when user is not authenticated', async () => { - authMock.mockImplementation((request: Request, response: Response, next) => { + authMock.mockImplementation((request, response, next) => { next(createError(401, 'No authorization token was found')) }); diff --git a/assets/express/example-app-auth0/test/todos/get.spec-e2e.ts b/express-pg-auth0/test/todos/get.spec-e2e.ts similarity index 94% rename from assets/express/example-app-auth0/test/todos/get.spec-e2e.ts rename to express-pg-auth0/test/todos/get.spec-e2e.ts index dce4cc9f..db49959f 100644 --- a/assets/express/example-app-auth0/test/todos/get.spec-e2e.ts +++ b/express-pg-auth0/test/todos/get.spec-e2e.ts @@ -49,8 +49,8 @@ describe('GET /v1/todos/:id', () => { authMock.mockImplementation( ( - req: Request & { auth: { payload: { sub: string } } }, - res: Response, + req: { auth: { payload: { sub: string } } }, + res, next: () => true ) => { req.auth = { @@ -103,7 +103,7 @@ describe('GET /v1/todos/:id', () => { describe('when user is not authenticated', () => { it('should return 401 error', async () => { - authMock.mockImplementation((request: Request, response: Response, next) => { + authMock.mockImplementation((request, response, next) => { next(createError(401, 'No authorization token was found')) }); diff --git a/assets/express/example-app-auth0/test/todos/list.spec-e2e.ts b/express-pg-auth0/test/todos/list.spec-e2e.ts similarity index 97% rename from assets/express/example-app-auth0/test/todos/list.spec-e2e.ts rename to express-pg-auth0/test/todos/list.spec-e2e.ts index 088de0a4..e4d206f6 100644 --- a/assets/express/example-app-auth0/test/todos/list.spec-e2e.ts +++ b/express-pg-auth0/test/todos/list.spec-e2e.ts @@ -41,8 +41,8 @@ describe('GET /v1/todos', () => { authMock.mockImplementation( ( - req: Request & { auth: { payload: { sub: string } } }, - res: Response, + req: { auth: { payload: { sub: string } } }, + res, next: () => true ) => { req.auth = { @@ -222,7 +222,7 @@ describe('GET /v1/todos', () => { describe('when user is not authenticated', () => { it('should return 401 error', async () => { - authMock.mockImplementation((request: Request, response: Response, next) => { + authMock.mockImplementation((request, response, next) => { next(createError(401, 'No authorization token was found')) }); diff --git a/assets/express/example-app-auth0/test/todos/update.spec-e2e.ts b/express-pg-auth0/test/todos/update.spec-e2e.ts similarity index 95% rename from assets/express/example-app-auth0/test/todos/update.spec-e2e.ts rename to express-pg-auth0/test/todos/update.spec-e2e.ts index d1b14d83..dfd83294 100644 --- a/assets/express/example-app-auth0/test/todos/update.spec-e2e.ts +++ b/express-pg-auth0/test/todos/update.spec-e2e.ts @@ -43,8 +43,8 @@ describe('POST /v1/todos/:id', () => { authMock.mockImplementation( ( - req: Request & { auth: { payload: { sub: string } } }, - res: Response, + req: { auth: { payload: { sub: string } } }, + res, next: () => true ) => { req.auth = { @@ -123,7 +123,7 @@ describe('POST /v1/todos/:id', () => { describe('when user is not authenticated', () => { it('should return 401 error', async () => { - authMock.mockImplementation((request: Request, response: Response, next) => { + authMock.mockImplementation((request, response, next) => { next(createError(401, 'No authorization token was found')) }); diff --git a/assets/express/example-app-auth0/test/utils/app.ts b/express-pg-auth0/test/utils/app.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/app.ts rename to express-pg-auth0/test/utils/app.ts diff --git a/assets/express/example-app-auth0/test/utils/errors.ts b/express-pg-auth0/test/utils/errors.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/errors.ts rename to express-pg-auth0/test/utils/errors.ts diff --git a/assets/express/example-app-auth0/test/utils/expect-error.ts b/express-pg-auth0/test/utils/expect-error.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/expect-error.ts rename to express-pg-auth0/test/utils/expect-error.ts diff --git a/assets/express/example-app-auth0/test/utils/get-todo-payload.ts b/express-pg-auth0/test/utils/get-todo-payload.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/get-todo-payload.ts rename to express-pg-auth0/test/utils/get-todo-payload.ts diff --git a/assets/express/example-app-auth0/test/utils/http-exception.ts b/express-pg-auth0/test/utils/http-exception.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/http-exception.ts rename to express-pg-auth0/test/utils/http-exception.ts diff --git a/assets/express/example-app-auth0/test/utils/index.ts b/express-pg-auth0/test/utils/index.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/index.ts rename to express-pg-auth0/test/utils/index.ts diff --git a/assets/express/example-app-auth0/test/utils/mock-axios.ts b/express-pg-auth0/test/utils/mock-axios.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/mock-axios.ts rename to express-pg-auth0/test/utils/mock-axios.ts diff --git a/assets/express/example-app-auth0/test/utils/sortby-field-todos.ts b/express-pg-auth0/test/utils/sortby-field-todos.ts similarity index 100% rename from assets/express/example-app-auth0/test/utils/sortby-field-todos.ts rename to express-pg-auth0/test/utils/sortby-field-todos.ts diff --git a/assets/tsconfig.build.json b/express-pg-auth0/tsconfig.build.json similarity index 100% rename from assets/tsconfig.build.json rename to express-pg-auth0/tsconfig.build.json diff --git a/assets/express/example-app-auth0/tsconfig.json b/express-pg-auth0/tsconfig.json similarity index 100% rename from assets/express/example-app-auth0/tsconfig.json rename to express-pg-auth0/tsconfig.json diff --git a/express-pg-jwt/.commitlintrc.js b/express-pg-jwt/.commitlintrc.js new file mode 100755 index 00000000..b0f2e40c --- /dev/null +++ b/express-pg-jwt/.commitlintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {}, + parserPreset: { + parserOpts: { + referenceActions: null, + issuePrefixes: ['AB#'], + }, + }, +}; diff --git a/express-pg-jwt/.czrc b/express-pg-jwt/.czrc new file mode 100644 index 00000000..64b8fecd --- /dev/null +++ b/express-pg-jwt/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/express-pg-jwt/.dockerignore b/express-pg-jwt/.dockerignore new file mode 100644 index 00000000..24da266d --- /dev/null +++ b/express-pg-jwt/.dockerignore @@ -0,0 +1,15 @@ +.env* +.eslint* +.git +.husky +.nvmrc +.prettier* +.vscode +coverage +coverage-e2e +dist +docker-compose* +Dockerfile +node_modules +README.md +test diff --git a/assets/.editorconfig b/express-pg-jwt/.editorconfig similarity index 100% rename from assets/.editorconfig rename to express-pg-jwt/.editorconfig diff --git a/express-pg-jwt/.env.example b/express-pg-jwt/.env.example new file mode 100644 index 00000000..6ee303d1 --- /dev/null +++ b/express-pg-jwt/.env.example @@ -0,0 +1,27 @@ +# Node +NODE_ENV=development + +# HTTP +PORT=3000 + +# Logging +ERROR_LOGGING=true +REQUEST_LOGGING=true + +# Swagger +SWAGGER=true + +# Knex +DEBUG=knex:query + +# PostgreSQL +PGHOST=localhost +PGPORT=5432 +PGUSER=user +PGPASSWORD=password +PGDATABASE=default + +# JWT +JWT_SECRET=super-secret +JWT_EXPIRATION=7200 + diff --git a/express-pg-jwt/.eslintignore b/express-pg-jwt/.eslintignore new file mode 100755 index 00000000..0589ba50 --- /dev/null +++ b/express-pg-jwt/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.vscode +coverage +dist/ +*.config.js +*rc.js +src/extensions/**/*.js +licenses-allowed.js diff --git a/express-pg-jwt/.eslintrc.js b/express-pg-jwt/.eslintrc.js new file mode 100644 index 00000000..4c17f68c --- /dev/null +++ b/express-pg-jwt/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + env: { + commonjs: false, + es2021: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:security/recommended', + 'prettier', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: {}, +}; diff --git a/express-pg-jwt/.github/workflows/audit.yaml b/express-pg-jwt/.github/workflows/audit.yaml new file mode 100644 index 00000000..d631240e --- /dev/null +++ b/express-pg-jwt/.github/workflows/audit.yaml @@ -0,0 +1,16 @@ +name: audit + +on: + pull_request: + branches: + - main + +jobs: + audit: + name: Audit packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run audit + run: npm audit --omit=dev diff --git a/express-pg-jwt/.github/workflows/coverage-e2e.yaml b/express-pg-jwt/.github/workflows/coverage-e2e.yaml new file mode 100644 index 00000000..599f71aa --- /dev/null +++ b/express-pg-jwt/.github/workflows/coverage-e2e.yaml @@ -0,0 +1,42 @@ +name: coverage-e2e + +on: + pull_request: + branches: + - main + +env: + NODE_ENV: test + DEBUG: '' + PORT: 3000 + PGHOST: localhost + PGPORT: 5432 + PGUSER: user + PGPASSWORD: password + PGDATABASE: tests + JWT_SECRET: super-secret + JWT_EXPIRATION: 7200 + +jobs: + coverage-e2e: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Create database + uses: isbang/compose-action@v1.4.1 + - name: Run coverage + uses: ArtiomTr/jest-coverage-report-action@v2 + with: + test-script: npm run test:e2e diff --git a/express-pg-jwt/.github/workflows/coverage.yaml b/express-pg-jwt/.github/workflows/coverage.yaml new file mode 100644 index 00000000..8de35d91 --- /dev/null +++ b/express-pg-jwt/.github/workflows/coverage.yaml @@ -0,0 +1,26 @@ +name: coverage + +on: + pull_request: + branches: + - main + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Get test coverage + uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/express-pg-jwt/.gitignore b/express-pg-jwt/.gitignore new file mode 100644 index 00000000..008b70cd --- /dev/null +++ b/express-pg-jwt/.gitignore @@ -0,0 +1,156 @@ +# This is a slightly modified version of https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# OS +.DS_Store + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage/ +coverage-e2e/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional prettier cache +.prettiercache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +# .env +.env* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Docker compose +docker-compose.override.yml + +# 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 diff --git a/express-pg-jwt/.husky/.gitignore b/express-pg-jwt/.husky/.gitignore new file mode 100644 index 00000000..c9cdc63b --- /dev/null +++ b/express-pg-jwt/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/express-pg-jwt/.husky/commit-msg b/express-pg-jwt/.husky/commit-msg new file mode 100755 index 00000000..9e8dfba1 --- /dev/null +++ b/express-pg-jwt/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit $1 diff --git a/express-pg-jwt/.husky/pre-commit b/express-pg-jwt/.husky/pre-commit new file mode 100755 index 00000000..2151bed3 --- /dev/null +++ b/express-pg-jwt/.husky/pre-commit @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- tsc --noEmit +npx --no -- lint-staged +npx --no -- ls-lint +npm run lint:dockerfile +npm run license:check +npm run test:cov +npm run test:e2e:cov +scripts/detect-secrets.sh + +# Enable branch naming validation by uncommenting the lines below and adjust valid_branch_regex to your preference +# # Enforce branch naming conventions +# LC_ALL=C + +# # Git branch will return an empty line before the initial commit +# git_branch=$(git branch) + +# if [ -z "$git_branch" ]; then +# echo "Initial commit, no branch name linting" +# exit 0 +# fi + +# # INSERT A REGEX PATTERN ACCORDING TO YOUR PROJECT BRANCH NAMING CONVENTION. +# branch_regex="^(chore|feat|fix|docs|style|refactor|test|revert|ci)\/[a-z0-9._-]+$" + +# # Get the branch name +# branch="$(git rev-parse --abbrev-ref HEAD)" + +# # UPDATE THE EXAMPLES TO MATCH YOUR PROJECT BRANCH NAMING CONVENTION. +# error_message="Branch name validation failed. Branch name must adhere to: /$branch_regex/. +# You should rename your branch to a valid name and try again. Here are some examples of valid branch names: + +# feat/my-feature +# test/my-feature +# fix/that-bug +# chore/my-task +# docs/update-readme +# refactor/do-thing-differently +# ci/update-build-script +# " + + +# # Use grep to do the matching, as `if [[ ]]` is not available in `sh` +# match=$(echo $branch | grep -E "$branch_regex") || echo "" + +# # Check if the match is a non-empty string +# if [ -z "$match" ]; then +# echo "$error_message" +# exit 1 +# fi diff --git a/express-pg-jwt/.husky/pre-push b/express-pg-jwt/.husky/pre-push new file mode 100755 index 00000000..b9c1cb2f --- /dev/null +++ b/express-pg-jwt/.husky/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" diff --git a/express-pg-jwt/.lintstagedrc b/express-pg-jwt/.lintstagedrc new file mode 100644 index 00000000..22e14328 --- /dev/null +++ b/express-pg-jwt/.lintstagedrc @@ -0,0 +1,12 @@ +{ + "*.{js,cjs,mjs,ts}": [ + "prettier --write", + "eslint --fix" + ], + "*.md": [ + "prettier --write", + "markdownlint --fix" + ], + "*.sh": "shellcheck -x", + "package.json": "sort-package-json" +} \ No newline at end of file diff --git a/assets/express/example-app/.ls-lint.yml b/express-pg-jwt/.ls-lint.yml similarity index 100% rename from assets/express/example-app/.ls-lint.yml rename to express-pg-jwt/.ls-lint.yml diff --git a/express-pg-jwt/.nvmrc b/express-pg-jwt/.nvmrc new file mode 100644 index 00000000..d3e33db8 --- /dev/null +++ b/express-pg-jwt/.nvmrc @@ -0,0 +1 @@ +v18.16 diff --git a/assets/express/example-app/.openapi/gitignorefile b/express-pg-jwt/.openapi/.gitignore similarity index 100% rename from assets/express/example-app/.openapi/gitignorefile rename to express-pg-jwt/.openapi/.gitignore diff --git a/express-pg-jwt/.prettierignore b/express-pg-jwt/.prettierignore new file mode 100644 index 00000000..b9a7a81d --- /dev/null +++ b/express-pg-jwt/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +coverage +coverage-e2e +*.config.js +*rc.js diff --git a/assets/.prettierrc.js b/express-pg-jwt/.prettierrc.js similarity index 100% rename from assets/.prettierrc.js rename to express-pg-jwt/.prettierrc.js diff --git a/express-pg-jwt/.secrets.baseline b/express-pg-jwt/.secrets.baseline new file mode 100644 index 00000000..524a5883 --- /dev/null +++ b/express-pg-jwt/.secrets.baseline @@ -0,0 +1,116 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-20T21:29:49Z" +} diff --git a/assets/express/example-app/Dockerfile b/express-pg-jwt/Dockerfile similarity index 100% rename from assets/express/example-app/Dockerfile rename to express-pg-jwt/Dockerfile diff --git a/express-pg-jwt/README.md b/express-pg-jwt/README.md new file mode 100644 index 00000000..a8f36e9c --- /dev/null +++ b/express-pg-jwt/README.md @@ -0,0 +1,330 @@ +# express-pg-jwt + +## Setup + +### Prerequisites + +#### Node and NPM via NVM + +**Install NVM:** + +Follow the instructions [here](https://github.com/nvm-sh/nvm#installing-and-updating). + +**Install the version of node specified in the `.nvmrc` file and switch to it:** + +```bash +nvm install +nvm use +``` + +**(Optional) Set the node version as default:** + +```bash +nvm alias default $(cat .nvmrc) +``` + +**(Optional) Automatically switch Node version when changing directories:** + +Follow the instructions for your shell [here](https://github.com/nvm-sh/nvm#deeper-shell-integration). + +#### Docker + +Follow the instructions [here](https://docs.docker.com/engine/install/). + +**Post installation steps for Linux users:** + +Follow the instructions [here](https://docs.docker.com/engine/install/linux-postinstall/). + +**Docker Compose:** + +In case `docker compose` isn't setup with the docker installation follow the +instructions [here](https://docs.docker.com/compose/install/). + +### Clone the repo and cd into the folder + +```bash +git clone git@github.com:.git +cd +``` + +### Install Node modules + +```bash +npm install +``` + +### Setup environment variables + +Copy the `.env.example` file to `.env`. Update environment variables as needed. + +```bash +cp .env.example .env +``` + +### Setup environment variables for e2e tests + +Copy the `.env` file to `.env.test`. Update environment variables as needed. + +```bash +cp .env .env.test +``` + +Update `NODE_ENV` to `test`. + +Make sure to provide a different database name from what you have in `.env` +as e2e tests wipe the database clean before they run. + +You may want to disable query logging by removing `DEBUG=knex:query`. + +### Setup docker-compose overrides + +Create a `docker-compose.override.yml` file using the example: + +```bash +cp docker-compose.override.example.yml docker-compose.override.yml +``` + +You can open the `docker-compose.override.yml` and adjust the port Swagger UI +is being served on (default is 3001). + +If you're on Apple M1, uncomment the `platform: linux/amd64` line: + +```yaml + swaggerui: + # uncomment the line below for Apple M1 + platform: linux/amd64 + ports: + - '3001:8080' +``` + + +### Provision the services + +In order to provision the services run: + +```bash +docker-compose up -d +``` + +If you want to provision Swagger UI as well, then use the `tools` profile: + +```bash +docker-compose --profile tools up -d +``` + +To learn more about using profiles, checkout the [documentation](https://docs.docker.com/compose/profiles/). + + +### Run database migrations + +```bash +npm run db:migrate:latest +``` + +## Running the app + +```bash +# build the app +npm run build +# run in development mode +npm run start + +# run in watch mode +npm run start:dev + +# run in debug mode +npm run start:debug +``` + +## Test + +### Unit Tests + +```bash +# run +npm run test + +# watch +npm run test:watch + +# coverage +npm run test:cov +``` + +### End-to-end Tests + +```bash +# e2e tests +npm run test:e2e + +# e2e coverage +npm run test:e2e:cov +``` + +## Working with migrations + +The underlying library is `knex`. You can find their guide on migrations [here](http://knexjs.org/guide/migrations.html). + +```bash +# check what is the last applied migration +npm run db:migrate:version + +# check which migrations have been run and how many are pending +npm run db:migrate:status + +# create a new migration file +npm run db:migrate:make + +# run the next migration that has not yet been run +npm run db:migrate:up + +# undo the last migration that was run +npm run db:migrate:down + +# run all pending migrations +npm run db:migrate:latest + +# rollback the last batch of migrations +npm run db:migrate:rollback + +# rollback all migrations +npm run db:migrate:rollback --all + +# rollback all migrations and re-apply latest +npm run db:migrate:reset +``` + +To debug migrations set the `DEBUG` environment variable to `knex:query`, e.g. + +```bash +DEBUG=knex:query npm run db:migrate:up +``` + +## Working with Docker + +```bash +# build the image +npm run image:build + +# run the image +npm run image:run +``` + +## Working with OpenAPI + +In order to generate the OpenAPI document run: + +```bash +npm run openapi:g +``` + +Then, if you haven't already, start the Swagger UI service: + +```bash +docker compose --profile tools up -d +``` + +Otherwise, restart the services: + +```bash +docker compose --profile tools restart +``` + +You only need to do this once, if you ran `docker-compose` before `npm run openapi:g`. + +Now navigate to `http://localhost:3001` and you should be able to see the `To-Do` +API documentation. + +If you're experiencing issues setting this up, checkout the troubleshooting +section at the bottom. + + +## Working with Licenses + +Whenever a new `npm` module is added to the project we need to check its +license. If the license is included in the +[OSI approved license list](https://opensource.org/licenses/), then we +can use it. If not - we need to review it manually. Start by running +the following `npm` script: + +```bash +npm run license:check +``` + +If the script succeeds, that means that the license is either a included +in the OSI list, or it was already reviewed and passed the review. When +the script fails it will print the first license that needs a review. +In order to list all licenses that need a review run the following: + +```bash +npm run license:for-review +``` + +In order to verify that a license suits your case, you can use a website like +[SOOS](https://app.soos.io/research/licenses). Find the license and inspect +the `Usage` and `Requirements` sections. You need to verify that the license +is `permissive`. Look for things like whether the license allows for +`Commercial Use` and `Private Use`. + +If the package passes the review, then include it in the list in +`licenses-reviewed.js` located in the root of ths project. + +If the package does not pass the review, then look for an alternative package. + +Lastly, if you just want to print a summary of all license, run: + +```bash +npm run license:summary +``` + +## Debug + +### VS Code + +Go to the Debug menu (CTRL+SHIFT+D). From `RUN AND DEBUG` at the top select +`Run Script: Launch via NPM`. You should now be able to start debugging +by pressing `F5`. + +## Troubleshooting +### OpenAPI + +If `docker-compose` fails with this message and you're on Apple M1: + +```bash +The requested image's platform (linux/amd64) does not match the detected host +platform (linux/arm64/v8) and no specific platform was requested +``` + +then uncomment the `platform: linux/amd64` line in `docker-compose.override.yml` + +```yaml + swaggerui: + # uncomment the line below for Apple M1 + platform: linux/amd64 + ports: + - '3001:8080' +``` + +If you're seeing `This site can't be reached` after navigating to `http://localhost:3001`, +then it means that the `swaggerui` service is not running. Try provisioning it: + +```bash +docker compose --profile tools up -d +``` + +In case you're seeing the `Swagger Petstore` documentation + +```bash +# generate the OpenApi document +npm run openapi:g + +# restart the services +docker compose --profile tools restart +``` + +If Swagger UI is running successfully, but all requests you make through it are failing, +then you probably forgot to start the application: + +```bash +npm run start:dev +``` diff --git a/assets/express/example-app/__mocks__/knex.ts b/express-pg-jwt/__mocks__/knex.ts similarity index 100% rename from assets/express/example-app/__mocks__/knex.ts rename to express-pg-jwt/__mocks__/knex.ts diff --git a/assets/express/example-app/docker-compose.override.example.yml b/express-pg-jwt/docker-compose.override.example.yml similarity index 100% rename from assets/express/example-app/docker-compose.override.example.yml rename to express-pg-jwt/docker-compose.override.example.yml diff --git a/assets/express/example-app/docker-compose.yml b/express-pg-jwt/docker-compose.yml similarity index 100% rename from assets/express/example-app/docker-compose.yml rename to express-pg-jwt/docker-compose.yml diff --git a/assets/express/example-app/jest.config.js b/express-pg-jwt/jest.config.js similarity index 100% rename from assets/express/example-app/jest.config.js rename to express-pg-jwt/jest.config.js diff --git a/assets/express/example-app-auth0/jest.setup.ts b/express-pg-jwt/jest.setup.ts similarity index 100% rename from assets/express/example-app-auth0/jest.setup.ts rename to express-pg-jwt/jest.setup.ts diff --git a/licenses-allowed.js b/express-pg-jwt/licenses-allowed.js similarity index 100% rename from licenses-allowed.js rename to express-pg-jwt/licenses-allowed.js diff --git a/licenses-reviewed.js b/express-pg-jwt/licenses-reviewed.js similarity index 100% rename from licenses-reviewed.js rename to express-pg-jwt/licenses-reviewed.js diff --git a/assets/express/example-app/migrations/20230302155244_initial.ts b/express-pg-jwt/migrations/20230302155244_initial.ts similarity index 100% rename from assets/express/example-app/migrations/20230302155244_initial.ts rename to express-pg-jwt/migrations/20230302155244_initial.ts diff --git a/assets/express/example-app/migrations/20230302162022_create_table_users.ts b/express-pg-jwt/migrations/20230302162022_create_table_users.ts similarity index 100% rename from assets/express/example-app/migrations/20230302162022_create_table_users.ts rename to express-pg-jwt/migrations/20230302162022_create_table_users.ts diff --git a/assets/express/example-app/migrations/20230302162023_create_table_todos.ts b/express-pg-jwt/migrations/20230302162023_create_table_todos.ts similarity index 100% rename from assets/express/example-app/migrations/20230302162023_create_table_todos.ts rename to express-pg-jwt/migrations/20230302162023_create_table_todos.ts diff --git a/assets/express/example-app/migrations/utils/index.ts b/express-pg-jwt/migrations/utils/index.ts similarity index 100% rename from assets/express/example-app/migrations/utils/index.ts rename to express-pg-jwt/migrations/utils/index.ts diff --git a/express-pg-jwt/nodemon.json b/express-pg-jwt/nodemon.json new file mode 100644 index 00000000..aa3eb0d1 --- /dev/null +++ b/express-pg-jwt/nodemon.json @@ -0,0 +1,12 @@ +{ + "watch": [ + "src" + ], + "ext": "ts", + "ignore": [ + "**/*.spec.*", + "**/*.test.*" + ], + "signal": "SIGINT", + "exec": "npx ts-node src/index" +} diff --git a/express-pg-jwt/package.json b/express-pg-jwt/package.json new file mode 100644 index 00000000..56721ec8 --- /dev/null +++ b/express-pg-jwt/package.json @@ -0,0 +1,111 @@ +{ + "name": "express-pg-jwt", + "version": "1.0.0", + "private": true, + "description": "", + "keywords": [], + "license": "ISC", + "author": "", + "main": "index.js", + "scripts": { + "build": "npm run clean && tsc -p tsconfig.build.json && tsc-alias", + "clean": "rimraf dist", + "db:connection:print": "ts-node scripts/db-connection", + "db:migrate:down": "ts-node node_modules/.bin/knex migrate:down --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:latest": "ts-node node_modules/.bin/knex migrate:latest --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:make": "knex migrate:make -x ts --migrations-directory ./migrations", + "db:migrate:reset": "npm run db:migrate:rollback --all && npm run db:migrate:latest", + "db:migrate:rollback": "ts-node node_modules/.bin/knex migrate:rollback --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:status": "ts-node node_modules/.bin/knex migrate:status --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:up": "ts-node node_modules/.bin/knex migrate:up --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:version": "ts-node node_modules/.bin/knex migrate:currentVersion --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "format": "prettier \"**/*.{js,cjs,mjs,ts,md}\" --write --cache --cache-strategy metadata --cache-location .prettiercache", + "image:build": "DOCKER_BUILDKIT=1 docker build -t express-pg-jwt .", + "image:run": "docker run --rm --net host -e NODE_ENV=production --env-file .env express-pg-jwt", + "license:check": "license-checker --summary --excludePrivatePackages --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --excludePrivatePackages --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint . --ext .js,.cjs,.mjs,.ts --fix --cache", + "lint:dockerfile": "docker run --rm -i hadolint/hadolint < Dockerfile", + "lint:markdown": "markdownlint **/*.md --ignore node_modules", + "openapi:g": "ts-node scripts/generate-openapi", + "prepare": "husky install", + "start": "npx ts-node src/index", + "start:debug": "tsc --sourceMap -p tsconfig.build.json && tsc-alias && node --inspect -r dotenv/config dist/index", + "start:dev": "nodemon", + "test": "jest", + "test:cov": "jest --coverage", + "test:e2e": "npx jest --config ./test/jest-e2e.config.js --runInBand", + "test:e2e:cov": "npm run test:e2e -- --coverage" + }, + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "bcrypt": "^5.1.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "express-jwt": "^8.4.1", + "helmet": "^6.0.1", + "http-errors": "^2.0.0", + "http-terminator": "^3.2.0", + "jsonwebtoken": "^9.0.0", + "knex": "^2.4.2", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "pino": "^8.11.0", + "query-types": "^0.1.4", + "statuses": "^2.0.1", + "zod": "^3.20.6", + "zod-openapi": "~2.2.2" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@ls-lint/ls-lint": "^1.11.2", + "@tsconfig/recommended": "^1.0.2", + "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.2", + "@types/cors": "^2.8.5", + "@types/express": "^4.17.17", + "@types/http-errors": "^2.0.1", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "^18.14.0", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-security": "~1.7.1", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "nodemon": "^3.0.2", + "pgtools": "^1.0.0", + "pino-pretty": "^9.4.0", + "prettier": "^2.8.4", + "rimraf": "^4.1.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "tsc-alias": "^1.8.2", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" + } +} \ No newline at end of file diff --git a/express-pg-jwt/scripts/db-connection.ts b/express-pg-jwt/scripts/db-connection.ts new file mode 100644 index 00000000..31acfe3c --- /dev/null +++ b/express-pg-jwt/scripts/db-connection.ts @@ -0,0 +1,3 @@ +console.log( + `postgres://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}` +); diff --git a/express-pg-jwt/scripts/detect-secrets.sh b/express-pg-jwt/scripts/detect-secrets.sh new file mode 100755 index 00000000..27d54428 --- /dev/null +++ b/express-pg-jwt/scripts/detect-secrets.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +RED=$'\e[0;31m' + +echo "[INFO] Running detect-secrets hook" + +# Exit if docker is not found +if ! command -v docker &> /dev/null +then + echo "${RED}[ERROR] docker could not be found! Docker is required in order to run detect-secrets" + exit 1 +fi + +echo "[INFO] If you encounter any issues with detect-secrets, please refer to https://github.com/Yelp/detect-secrets" + +VOLUME_PATH="--volume $(pwd):/usr/src/app" +EXCLUDED_FILES="--exclude-files .*\.(test|spec|e2e)(-e2e|-spec)?\.(js|ts)$ --exclude-files .github/workflows/coverage-e2e.yaml" +STAGED_FILES_PATHS=$(git diff --staged --name-only -z | xargs -0) + +# shellcheck disable=SC2086 +if ! docker run --rm --name detect-secrets $VOLUME_PATH lirantal/detect-secrets $EXCLUDED_FILES --baseline .secrets.baseline $STAGED_FILES_PATHS; +then + echo "${RED}[ERROR] Remove secrets before commiting." + exit 1 +fi + +echo "[INFO] detect-secrets hook completed succesfully. No secrets found!" \ No newline at end of file diff --git a/assets/express/example-app/scripts/generate-openapi.ts b/express-pg-jwt/scripts/generate-openapi.ts similarity index 100% rename from assets/express/example-app/scripts/generate-openapi.ts rename to express-pg-jwt/scripts/generate-openapi.ts diff --git a/assets/express/example-app/src/@types/express/index.d.ts b/express-pg-jwt/src/@types/express/index.d.ts similarity index 100% rename from assets/express/example-app/src/@types/express/index.d.ts rename to express-pg-jwt/src/@types/express/index.d.ts diff --git a/assets/express/example-app/src/@types/knex/index.d.ts b/express-pg-jwt/src/@types/knex/index.d.ts similarity index 100% rename from assets/express/example-app/src/@types/knex/index.d.ts rename to express-pg-jwt/src/@types/knex/index.d.ts diff --git a/assets/express/example-app/src/@types/query-types/index.d.ts b/express-pg-jwt/src/@types/query-types/index.d.ts similarity index 100% rename from assets/express/example-app/src/@types/query-types/index.d.ts rename to express-pg-jwt/src/@types/query-types/index.d.ts diff --git a/assets/express/example-app/src/api/auth/entities/credentials.entity.ts b/express-pg-jwt/src/api/auth/entities/credentials.entity.ts similarity index 100% rename from assets/express/example-app/src/api/auth/entities/credentials.entity.ts rename to express-pg-jwt/src/api/auth/entities/credentials.entity.ts diff --git a/assets/express/example-app/src/api/auth/entities/index.ts b/express-pg-jwt/src/api/auth/entities/index.ts similarity index 100% rename from assets/express/example-app/src/api/auth/entities/index.ts rename to express-pg-jwt/src/api/auth/entities/index.ts diff --git a/assets/express/example-app/src/api/auth/entities/jwt-claims.entity.ts b/express-pg-jwt/src/api/auth/entities/jwt-claims.entity.ts similarity index 100% rename from assets/express/example-app/src/api/auth/entities/jwt-claims.entity.ts rename to express-pg-jwt/src/api/auth/entities/jwt-claims.entity.ts diff --git a/assets/express/example-app/src/api/auth/entities/jwt-tokens.entity.ts b/express-pg-jwt/src/api/auth/entities/jwt-tokens.entity.ts similarity index 100% rename from assets/express/example-app/src/api/auth/entities/jwt-tokens.entity.ts rename to express-pg-jwt/src/api/auth/entities/jwt-tokens.entity.ts diff --git a/assets/express/example-app/src/api/auth/index.ts b/express-pg-jwt/src/api/auth/index.ts similarity index 100% rename from assets/express/example-app/src/api/auth/index.ts rename to express-pg-jwt/src/api/auth/index.ts diff --git a/assets/express/example-app/src/api/auth/routes/index.ts b/express-pg-jwt/src/api/auth/routes/index.ts similarity index 100% rename from assets/express/example-app/src/api/auth/routes/index.ts rename to express-pg-jwt/src/api/auth/routes/index.ts diff --git a/assets/express/example-app/src/api/auth/routes/login.route.test.ts b/express-pg-jwt/src/api/auth/routes/login.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/auth/routes/login.route.test.ts rename to express-pg-jwt/src/api/auth/routes/login.route.test.ts diff --git a/assets/express/example-app/src/api/auth/routes/login.route.ts b/express-pg-jwt/src/api/auth/routes/login.route.ts similarity index 100% rename from assets/express/example-app/src/api/auth/routes/login.route.ts rename to express-pg-jwt/src/api/auth/routes/login.route.ts diff --git a/assets/express/example-app/src/api/auth/routes/register.route.test.ts b/express-pg-jwt/src/api/auth/routes/register.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/auth/routes/register.route.test.ts rename to express-pg-jwt/src/api/auth/routes/register.route.test.ts diff --git a/assets/express/example-app/src/api/auth/routes/register.route.ts b/express-pg-jwt/src/api/auth/routes/register.route.ts similarity index 100% rename from assets/express/example-app/src/api/auth/routes/register.route.ts rename to express-pg-jwt/src/api/auth/routes/register.route.ts diff --git a/assets/express/example-app/src/api/auth/schemas/credentials.schema.ts b/express-pg-jwt/src/api/auth/schemas/credentials.schema.ts similarity index 100% rename from assets/express/example-app/src/api/auth/schemas/credentials.schema.ts rename to express-pg-jwt/src/api/auth/schemas/credentials.schema.ts diff --git a/assets/express/example-app/src/api/auth/schemas/index.ts b/express-pg-jwt/src/api/auth/schemas/index.ts similarity index 100% rename from assets/express/example-app/src/api/auth/schemas/index.ts rename to express-pg-jwt/src/api/auth/schemas/index.ts diff --git a/assets/express/example-app/src/api/auth/schemas/jwt-tokens.schema.ts b/express-pg-jwt/src/api/auth/schemas/jwt-tokens.schema.ts similarity index 100% rename from assets/express/example-app/src/api/auth/schemas/jwt-tokens.schema.ts rename to express-pg-jwt/src/api/auth/schemas/jwt-tokens.schema.ts diff --git a/assets/express/example-app/src/api/auth/services/auth.service.test.ts b/express-pg-jwt/src/api/auth/services/auth.service.test.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/auth.service.test.ts rename to express-pg-jwt/src/api/auth/services/auth.service.test.ts diff --git a/assets/express/example-app/src/api/auth/services/auth.service.ts b/express-pg-jwt/src/api/auth/services/auth.service.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/auth.service.ts rename to express-pg-jwt/src/api/auth/services/auth.service.ts diff --git a/assets/express/example-app/src/api/auth/services/index.ts b/express-pg-jwt/src/api/auth/services/index.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/index.ts rename to express-pg-jwt/src/api/auth/services/index.ts diff --git a/assets/express/example-app/src/api/auth/services/jwt.service.test.ts b/express-pg-jwt/src/api/auth/services/jwt.service.test.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/jwt.service.test.ts rename to express-pg-jwt/src/api/auth/services/jwt.service.test.ts diff --git a/assets/express/example-app/src/api/auth/services/jwt.service.ts b/express-pg-jwt/src/api/auth/services/jwt.service.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/jwt.service.ts rename to express-pg-jwt/src/api/auth/services/jwt.service.ts diff --git a/assets/express/example-app/src/api/auth/services/password.service.test.ts b/express-pg-jwt/src/api/auth/services/password.service.test.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/password.service.test.ts rename to express-pg-jwt/src/api/auth/services/password.service.test.ts diff --git a/assets/express/example-app/src/api/auth/services/password.service.ts b/express-pg-jwt/src/api/auth/services/password.service.ts similarity index 100% rename from assets/express/example-app/src/api/auth/services/password.service.ts rename to express-pg-jwt/src/api/auth/services/password.service.ts diff --git a/assets/express/example-app/src/api/healthchecks/index.ts b/express-pg-jwt/src/api/healthchecks/index.ts similarity index 100% rename from assets/express/example-app/src/api/healthchecks/index.ts rename to express-pg-jwt/src/api/healthchecks/index.ts diff --git a/assets/express/example-app/src/api/healthchecks/routes/index.ts b/express-pg-jwt/src/api/healthchecks/routes/index.ts similarity index 100% rename from assets/express/example-app/src/api/healthchecks/routes/index.ts rename to express-pg-jwt/src/api/healthchecks/routes/index.ts diff --git a/assets/express/example-app/src/api/healthchecks/routes/live.route.test.ts b/express-pg-jwt/src/api/healthchecks/routes/live.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/healthchecks/routes/live.route.test.ts rename to express-pg-jwt/src/api/healthchecks/routes/live.route.test.ts diff --git a/assets/express/example-app/src/api/healthchecks/routes/live.route.ts b/express-pg-jwt/src/api/healthchecks/routes/live.route.ts similarity index 100% rename from assets/express/example-app/src/api/healthchecks/routes/live.route.ts rename to express-pg-jwt/src/api/healthchecks/routes/live.route.ts diff --git a/assets/express/example-app/src/api/healthchecks/routes/ready.route.test.ts b/express-pg-jwt/src/api/healthchecks/routes/ready.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/healthchecks/routes/ready.route.test.ts rename to express-pg-jwt/src/api/healthchecks/routes/ready.route.test.ts diff --git a/assets/express/example-app/src/api/healthchecks/routes/ready.route.ts b/express-pg-jwt/src/api/healthchecks/routes/ready.route.ts similarity index 100% rename from assets/express/example-app/src/api/healthchecks/routes/ready.route.ts rename to express-pg-jwt/src/api/healthchecks/routes/ready.route.ts diff --git a/assets/express/example-app/src/api/todos/entities/index.ts b/express-pg-jwt/src/api/todos/entities/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/entities/index.ts rename to express-pg-jwt/src/api/todos/entities/index.ts diff --git a/assets/express/example-app/src/api/todos/entities/insert-todo.entity.ts b/express-pg-jwt/src/api/todos/entities/insert-todo.entity.ts similarity index 100% rename from assets/express/example-app/src/api/todos/entities/insert-todo.entity.ts rename to express-pg-jwt/src/api/todos/entities/insert-todo.entity.ts diff --git a/assets/express/example-app/src/api/todos/entities/list-todos-query.entity.ts b/express-pg-jwt/src/api/todos/entities/list-todos-query.entity.ts similarity index 100% rename from assets/express/example-app/src/api/todos/entities/list-todos-query.entity.ts rename to express-pg-jwt/src/api/todos/entities/list-todos-query.entity.ts diff --git a/assets/express/example-app/src/api/todos/entities/todo.entity.ts b/express-pg-jwt/src/api/todos/entities/todo.entity.ts similarity index 100% rename from assets/express/example-app/src/api/todos/entities/todo.entity.ts rename to express-pg-jwt/src/api/todos/entities/todo.entity.ts diff --git a/assets/express/example-app/src/api/todos/entities/update-todo.entity.ts b/express-pg-jwt/src/api/todos/entities/update-todo.entity.ts similarity index 100% rename from assets/express/example-app/src/api/todos/entities/update-todo.entity.ts rename to express-pg-jwt/src/api/todos/entities/update-todo.entity.ts diff --git a/assets/express/example-app/src/api/todos/error-mappings/index.ts b/express-pg-jwt/src/api/todos/error-mappings/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/error-mappings/index.ts rename to express-pg-jwt/src/api/todos/error-mappings/index.ts diff --git a/assets/express/example-app/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/express-pg-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts similarity index 100% rename from assets/express/example-app/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts rename to express-pg-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts diff --git a/assets/express/example-app/src/api/todos/filters/index.ts b/express-pg-jwt/src/api/todos/filters/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/filters/index.ts rename to express-pg-jwt/src/api/todos/filters/index.ts diff --git a/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.test.ts b/express-pg-jwt/src/api/todos/filters/todos-by-completed.filter.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.test.ts rename to express-pg-jwt/src/api/todos/filters/todos-by-completed.filter.test.ts diff --git a/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.ts b/express-pg-jwt/src/api/todos/filters/todos-by-completed.filter.ts similarity index 100% rename from assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.ts rename to express-pg-jwt/src/api/todos/filters/todos-by-completed.filter.ts diff --git a/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.test.ts b/express-pg-jwt/src/api/todos/filters/todos-by-name.filter.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/filters/todos-by-name.filter.test.ts rename to express-pg-jwt/src/api/todos/filters/todos-by-name.filter.test.ts diff --git a/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.ts b/express-pg-jwt/src/api/todos/filters/todos-by-name.filter.ts similarity index 100% rename from assets/express/example-app/src/api/todos/filters/todos-by-name.filter.ts rename to express-pg-jwt/src/api/todos/filters/todos-by-name.filter.ts diff --git a/assets/express/example-app/src/api/todos/index.ts b/express-pg-jwt/src/api/todos/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/index.ts rename to express-pg-jwt/src/api/todos/index.ts diff --git a/assets/express/example-app/src/api/todos/repositories/__mocks__/todos.repository.ts b/express-pg-jwt/src/api/todos/repositories/__mocks__/todos.repository.ts similarity index 100% rename from assets/express/example-app/src/api/todos/repositories/__mocks__/todos.repository.ts rename to express-pg-jwt/src/api/todos/repositories/__mocks__/todos.repository.ts diff --git a/assets/express/example-app/src/api/todos/repositories/index.ts b/express-pg-jwt/src/api/todos/repositories/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/repositories/index.ts rename to express-pg-jwt/src/api/todos/repositories/index.ts diff --git a/assets/express/example-app/src/api/todos/repositories/todos.repository.test.ts b/express-pg-jwt/src/api/todos/repositories/todos.repository.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/repositories/todos.repository.test.ts rename to express-pg-jwt/src/api/todos/repositories/todos.repository.test.ts diff --git a/assets/express/example-app/src/api/todos/repositories/todos.repository.ts b/express-pg-jwt/src/api/todos/repositories/todos.repository.ts similarity index 100% rename from assets/express/example-app/src/api/todos/repositories/todos.repository.ts rename to express-pg-jwt/src/api/todos/repositories/todos.repository.ts diff --git a/assets/express/example-app/src/api/todos/routes/create.route.test.ts b/express-pg-jwt/src/api/todos/routes/create.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/create.route.test.ts rename to express-pg-jwt/src/api/todos/routes/create.route.test.ts diff --git a/assets/express/example-app/src/api/todos/routes/create.route.ts b/express-pg-jwt/src/api/todos/routes/create.route.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/create.route.ts rename to express-pg-jwt/src/api/todos/routes/create.route.ts diff --git a/assets/express/example-app/src/api/todos/routes/delete.route.test.ts b/express-pg-jwt/src/api/todos/routes/delete.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/delete.route.test.ts rename to express-pg-jwt/src/api/todos/routes/delete.route.test.ts diff --git a/assets/express/example-app/src/api/todos/routes/delete.route.ts b/express-pg-jwt/src/api/todos/routes/delete.route.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/delete.route.ts rename to express-pg-jwt/src/api/todos/routes/delete.route.ts diff --git a/assets/express/example-app/src/api/todos/routes/get.route.test.ts b/express-pg-jwt/src/api/todos/routes/get.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/get.route.test.ts rename to express-pg-jwt/src/api/todos/routes/get.route.test.ts diff --git a/assets/express/example-app/src/api/todos/routes/get.route.ts b/express-pg-jwt/src/api/todos/routes/get.route.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/get.route.ts rename to express-pg-jwt/src/api/todos/routes/get.route.ts diff --git a/assets/express/example-app/src/api/todos/routes/index.ts b/express-pg-jwt/src/api/todos/routes/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/index.ts rename to express-pg-jwt/src/api/todos/routes/index.ts diff --git a/assets/express/example-app/src/api/todos/routes/list.route.test.ts b/express-pg-jwt/src/api/todos/routes/list.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/list.route.test.ts rename to express-pg-jwt/src/api/todos/routes/list.route.test.ts diff --git a/assets/express/example-app/src/api/todos/routes/list.route.ts b/express-pg-jwt/src/api/todos/routes/list.route.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/list.route.ts rename to express-pg-jwt/src/api/todos/routes/list.route.ts diff --git a/assets/express/example-app/src/api/todos/routes/update.route.test.ts b/express-pg-jwt/src/api/todos/routes/update.route.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/update.route.test.ts rename to express-pg-jwt/src/api/todos/routes/update.route.test.ts diff --git a/assets/express/example-app/src/api/todos/routes/update.route.ts b/express-pg-jwt/src/api/todos/routes/update.route.ts similarity index 100% rename from assets/express/example-app/src/api/todos/routes/update.route.ts rename to express-pg-jwt/src/api/todos/routes/update.route.ts diff --git a/assets/express/example-app/src/api/todos/schemas/create-todo.schema.ts b/express-pg-jwt/src/api/todos/schemas/create-todo.schema.ts similarity index 100% rename from assets/express/example-app/src/api/todos/schemas/create-todo.schema.ts rename to express-pg-jwt/src/api/todos/schemas/create-todo.schema.ts diff --git a/assets/express/example-app/src/api/todos/schemas/index.ts b/express-pg-jwt/src/api/todos/schemas/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/schemas/index.ts rename to express-pg-jwt/src/api/todos/schemas/index.ts diff --git a/assets/express/example-app/src/api/todos/schemas/list-todos-query.schema.ts b/express-pg-jwt/src/api/todos/schemas/list-todos-query.schema.ts similarity index 100% rename from assets/express/example-app/src/api/todos/schemas/list-todos-query.schema.ts rename to express-pg-jwt/src/api/todos/schemas/list-todos-query.schema.ts diff --git a/assets/express/example-app/src/api/todos/schemas/todo-id-param.schema.ts b/express-pg-jwt/src/api/todos/schemas/todo-id-param.schema.ts similarity index 100% rename from assets/express/example-app/src/api/todos/schemas/todo-id-param.schema.ts rename to express-pg-jwt/src/api/todos/schemas/todo-id-param.schema.ts diff --git a/assets/express/example-app/src/api/todos/schemas/todo.schema.ts b/express-pg-jwt/src/api/todos/schemas/todo.schema.ts similarity index 100% rename from assets/express/example-app/src/api/todos/schemas/todo.schema.ts rename to express-pg-jwt/src/api/todos/schemas/todo.schema.ts diff --git a/assets/express/example-app/src/api/todos/schemas/update-todo.schema.ts b/express-pg-jwt/src/api/todos/schemas/update-todo.schema.ts similarity index 100% rename from assets/express/example-app/src/api/todos/schemas/update-todo.schema.ts rename to express-pg-jwt/src/api/todos/schemas/update-todo.schema.ts diff --git a/assets/express/example-app/src/api/todos/services/index.ts b/express-pg-jwt/src/api/todos/services/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/services/index.ts rename to express-pg-jwt/src/api/todos/services/index.ts diff --git a/assets/express/example-app/src/api/todos/services/todos.service.test.ts b/express-pg-jwt/src/api/todos/services/todos.service.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/services/todos.service.test.ts rename to express-pg-jwt/src/api/todos/services/todos.service.test.ts diff --git a/assets/express/example-app/src/api/todos/services/todos.service.ts b/express-pg-jwt/src/api/todos/services/todos.service.ts similarity index 100% rename from assets/express/example-app/src/api/todos/services/todos.service.ts rename to express-pg-jwt/src/api/todos/services/todos.service.ts diff --git a/assets/express/example-app/src/api/todos/sorters/index.ts b/express-pg-jwt/src/api/todos/sorters/index.ts similarity index 100% rename from assets/express/example-app/src/api/todos/sorters/index.ts rename to express-pg-jwt/src/api/todos/sorters/index.ts diff --git a/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.test.ts b/express-pg-jwt/src/api/todos/sorters/todos-by-created-at.sorter.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.test.ts rename to express-pg-jwt/src/api/todos/sorters/todos-by-created-at.sorter.test.ts diff --git a/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.ts b/express-pg-jwt/src/api/todos/sorters/todos-by-created-at.sorter.ts similarity index 100% rename from assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.ts rename to express-pg-jwt/src/api/todos/sorters/todos-by-created-at.sorter.ts diff --git a/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.test.ts b/express-pg-jwt/src/api/todos/sorters/todos-by-name.sorter.test.ts similarity index 100% rename from assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.test.ts rename to express-pg-jwt/src/api/todos/sorters/todos-by-name.sorter.test.ts diff --git a/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.ts b/express-pg-jwt/src/api/todos/sorters/todos-by-name.sorter.ts similarity index 100% rename from assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.ts rename to express-pg-jwt/src/api/todos/sorters/todos-by-name.sorter.ts diff --git a/assets/express/example-app/src/api/users/entities/index.ts b/express-pg-jwt/src/api/users/entities/index.ts similarity index 100% rename from assets/express/example-app/src/api/users/entities/index.ts rename to express-pg-jwt/src/api/users/entities/index.ts diff --git a/assets/express/example-app/src/api/users/entities/insert-user.entity.ts b/express-pg-jwt/src/api/users/entities/insert-user.entity.ts similarity index 100% rename from assets/express/example-app/src/api/users/entities/insert-user.entity.ts rename to express-pg-jwt/src/api/users/entities/insert-user.entity.ts diff --git a/assets/express/example-app/src/api/users/entities/update-user.entity.ts b/express-pg-jwt/src/api/users/entities/update-user.entity.ts similarity index 100% rename from assets/express/example-app/src/api/users/entities/update-user.entity.ts rename to express-pg-jwt/src/api/users/entities/update-user.entity.ts diff --git a/assets/express/example-app/src/api/users/entities/user.entity.ts b/express-pg-jwt/src/api/users/entities/user.entity.ts similarity index 100% rename from assets/express/example-app/src/api/users/entities/user.entity.ts rename to express-pg-jwt/src/api/users/entities/user.entity.ts diff --git a/assets/express/example-app/src/api/users/error-mappings/index.ts b/express-pg-jwt/src/api/users/error-mappings/index.ts similarity index 100% rename from assets/express/example-app/src/api/users/error-mappings/index.ts rename to express-pg-jwt/src/api/users/error-mappings/index.ts diff --git a/assets/express/example-app/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/express-pg-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts similarity index 100% rename from assets/express/example-app/src/api/users/error-mappings/user-email-taken.error-mapping.ts rename to express-pg-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts diff --git a/assets/express/example-app/src/api/users/index.ts b/express-pg-jwt/src/api/users/index.ts similarity index 100% rename from assets/express/example-app/src/api/users/index.ts rename to express-pg-jwt/src/api/users/index.ts diff --git a/assets/express/example-app/src/api/users/repositories/__mocks__/users.repository.ts b/express-pg-jwt/src/api/users/repositories/__mocks__/users.repository.ts similarity index 100% rename from assets/express/example-app/src/api/users/repositories/__mocks__/users.repository.ts rename to express-pg-jwt/src/api/users/repositories/__mocks__/users.repository.ts diff --git a/assets/express/example-app/src/api/users/repositories/index.ts b/express-pg-jwt/src/api/users/repositories/index.ts similarity index 100% rename from assets/express/example-app/src/api/users/repositories/index.ts rename to express-pg-jwt/src/api/users/repositories/index.ts diff --git a/assets/express/example-app/src/api/users/repositories/users.repository.test.ts b/express-pg-jwt/src/api/users/repositories/users.repository.test.ts similarity index 100% rename from assets/express/example-app/src/api/users/repositories/users.repository.test.ts rename to express-pg-jwt/src/api/users/repositories/users.repository.test.ts diff --git a/assets/express/example-app/src/api/users/repositories/users.repository.ts b/express-pg-jwt/src/api/users/repositories/users.repository.ts similarity index 100% rename from assets/express/example-app/src/api/users/repositories/users.repository.ts rename to express-pg-jwt/src/api/users/repositories/users.repository.ts diff --git a/assets/express/example-app/src/api/users/schemas/index.ts b/express-pg-jwt/src/api/users/schemas/index.ts similarity index 100% rename from assets/express/example-app/src/api/users/schemas/index.ts rename to express-pg-jwt/src/api/users/schemas/index.ts diff --git a/assets/express/example-app/src/api/users/schemas/user.schema.ts b/express-pg-jwt/src/api/users/schemas/user.schema.ts similarity index 100% rename from assets/express/example-app/src/api/users/schemas/user.schema.ts rename to express-pg-jwt/src/api/users/schemas/user.schema.ts diff --git a/assets/express/example-app/src/app.test.ts b/express-pg-jwt/src/app.test.ts similarity index 100% rename from assets/express/example-app/src/app.test.ts rename to express-pg-jwt/src/app.test.ts diff --git a/assets/express/example-app/src/app.ts b/express-pg-jwt/src/app.ts similarity index 100% rename from assets/express/example-app/src/app.ts rename to express-pg-jwt/src/app.ts diff --git a/assets/express/example-app/src/database/client.test.ts b/express-pg-jwt/src/database/client.test.ts similarity index 100% rename from assets/express/example-app/src/database/client.test.ts rename to express-pg-jwt/src/database/client.test.ts diff --git a/assets/express/example-app/src/database/client.ts b/express-pg-jwt/src/database/client.ts similarity index 100% rename from assets/express/example-app/src/database/client.ts rename to express-pg-jwt/src/database/client.ts diff --git a/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts b/express-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts similarity index 100% rename from assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts rename to express-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts diff --git a/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/express-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.ts similarity index 100% rename from assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.ts rename to express-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.ts diff --git a/assets/express/example-app/src/database/error-mappings/index.ts b/express-pg-jwt/src/database/error-mappings/index.ts similarity index 100% rename from assets/express/example-app/src/database/error-mappings/index.ts rename to express-pg-jwt/src/database/error-mappings/index.ts diff --git a/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.test.ts b/express-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.test.ts similarity index 100% rename from assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.test.ts rename to express-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.test.ts diff --git a/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.ts b/express-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.ts similarity index 100% rename from assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.ts rename to express-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.ts diff --git a/assets/express/example-app/src/database/errors/duplicate-record.error.ts b/express-pg-jwt/src/database/errors/duplicate-record.error.ts similarity index 100% rename from assets/express/example-app/src/database/errors/duplicate-record.error.ts rename to express-pg-jwt/src/database/errors/duplicate-record.error.ts diff --git a/assets/express/example-app/src/database/errors/index.ts b/express-pg-jwt/src/database/errors/index.ts similarity index 100% rename from assets/express/example-app/src/database/errors/index.ts rename to express-pg-jwt/src/database/errors/index.ts diff --git a/assets/express/example-app/src/database/errors/record-not-found.error.ts b/express-pg-jwt/src/database/errors/record-not-found.error.ts similarity index 100% rename from assets/express/example-app/src/database/errors/record-not-found.error.ts rename to express-pg-jwt/src/database/errors/record-not-found.error.ts diff --git a/assets/express/example-app/src/database/index.ts b/express-pg-jwt/src/database/index.ts similarity index 100% rename from assets/express/example-app/src/database/index.ts rename to express-pg-jwt/src/database/index.ts diff --git a/assets/express/example-app/src/database/on-init.ts b/express-pg-jwt/src/database/on-init.ts similarity index 100% rename from assets/express/example-app/src/database/on-init.ts rename to express-pg-jwt/src/database/on-init.ts diff --git a/assets/express/example-app/src/database/operations.ts b/express-pg-jwt/src/database/operations.ts similarity index 100% rename from assets/express/example-app/src/database/operations.ts rename to express-pg-jwt/src/database/operations.ts diff --git a/assets/express/example-app/src/database/query-builder/extensions.test.ts b/express-pg-jwt/src/database/query-builder/extensions.test.ts similarity index 100% rename from assets/express/example-app/src/database/query-builder/extensions.test.ts rename to express-pg-jwt/src/database/query-builder/extensions.test.ts diff --git a/assets/express/example-app/src/database/query-builder/extensions.ts b/express-pg-jwt/src/database/query-builder/extensions.ts similarity index 100% rename from assets/express/example-app/src/database/query-builder/extensions.ts rename to express-pg-jwt/src/database/query-builder/extensions.ts diff --git a/assets/express/example-app/src/database/query-builder/index.ts b/express-pg-jwt/src/database/query-builder/index.ts similarity index 100% rename from assets/express/example-app/src/database/query-builder/index.ts rename to express-pg-jwt/src/database/query-builder/index.ts diff --git a/assets/express/example-app/src/database/seeds/test/index.ts b/express-pg-jwt/src/database/seeds/test/index.ts similarity index 100% rename from assets/express/example-app/src/database/seeds/test/index.ts rename to express-pg-jwt/src/database/seeds/test/index.ts diff --git a/assets/express/example-app/src/database/utils/error.ts b/express-pg-jwt/src/database/utils/error.ts similarity index 100% rename from assets/express/example-app/src/database/utils/error.ts rename to express-pg-jwt/src/database/utils/error.ts diff --git a/assets/express/example-app/src/database/utils/index.ts b/express-pg-jwt/src/database/utils/index.ts similarity index 100% rename from assets/express/example-app/src/database/utils/index.ts rename to express-pg-jwt/src/database/utils/index.ts diff --git a/assets/express/example-app/src/extensions/knex/index.ts b/express-pg-jwt/src/extensions/knex/index.ts similarity index 100% rename from assets/express/example-app/src/extensions/knex/index.ts rename to express-pg-jwt/src/extensions/knex/index.ts diff --git a/assets/express/example-app/src/extensions/knex/register.ts b/express-pg-jwt/src/extensions/knex/register.ts similarity index 100% rename from assets/express/example-app/src/extensions/knex/register.ts rename to express-pg-jwt/src/extensions/knex/register.ts diff --git a/assets/express/example-app/src/extensions/zod/index.ts b/express-pg-jwt/src/extensions/zod/index.ts similarity index 100% rename from assets/express/example-app/src/extensions/zod/index.ts rename to express-pg-jwt/src/extensions/zod/index.ts diff --git a/assets/express/example-app/src/extensions/zod/register.ts b/express-pg-jwt/src/extensions/zod/register.ts similarity index 100% rename from assets/express/example-app/src/extensions/zod/register.ts rename to express-pg-jwt/src/extensions/zod/register.ts diff --git a/assets/express/example-app/src/index.ts b/express-pg-jwt/src/index.ts similarity index 100% rename from assets/express/example-app/src/index.ts rename to express-pg-jwt/src/index.ts diff --git a/assets/express/example-app/src/middleware/attach-services.middleware.test.ts b/express-pg-jwt/src/middleware/attach-services.middleware.test.ts similarity index 100% rename from assets/express/example-app/src/middleware/attach-services.middleware.test.ts rename to express-pg-jwt/src/middleware/attach-services.middleware.test.ts diff --git a/assets/express/example-app/src/middleware/attach-services.middleware.ts b/express-pg-jwt/src/middleware/attach-services.middleware.ts similarity index 100% rename from assets/express/example-app/src/middleware/attach-services.middleware.ts rename to express-pg-jwt/src/middleware/attach-services.middleware.ts diff --git a/assets/express/example-app/src/middleware/handle-error.middleware.test.ts b/express-pg-jwt/src/middleware/handle-error.middleware.test.ts similarity index 100% rename from assets/express/example-app/src/middleware/handle-error.middleware.test.ts rename to express-pg-jwt/src/middleware/handle-error.middleware.test.ts diff --git a/assets/express/example-app/src/middleware/handle-error.middleware.ts b/express-pg-jwt/src/middleware/handle-error.middleware.ts similarity index 100% rename from assets/express/example-app/src/middleware/handle-error.middleware.ts rename to express-pg-jwt/src/middleware/handle-error.middleware.ts diff --git a/assets/express/example-app/src/middleware/index.ts b/express-pg-jwt/src/middleware/index.ts similarity index 100% rename from assets/express/example-app/src/middleware/index.ts rename to express-pg-jwt/src/middleware/index.ts diff --git a/assets/express/example-app/src/middleware/log-request.middleware.test.ts b/express-pg-jwt/src/middleware/log-request.middleware.test.ts similarity index 100% rename from assets/express/example-app/src/middleware/log-request.middleware.test.ts rename to express-pg-jwt/src/middleware/log-request.middleware.test.ts diff --git a/assets/express/example-app/src/middleware/log-request.middleware.ts b/express-pg-jwt/src/middleware/log-request.middleware.ts similarity index 100% rename from assets/express/example-app/src/middleware/log-request.middleware.ts rename to express-pg-jwt/src/middleware/log-request.middleware.ts diff --git a/assets/express/example-app/src/middleware/map-error.middleware.test.ts b/express-pg-jwt/src/middleware/map-error.middleware.test.ts similarity index 100% rename from assets/express/example-app/src/middleware/map-error.middleware.test.ts rename to express-pg-jwt/src/middleware/map-error.middleware.test.ts diff --git a/assets/express/example-app/src/middleware/map-error.middleware.ts b/express-pg-jwt/src/middleware/map-error.middleware.ts similarity index 100% rename from assets/express/example-app/src/middleware/map-error.middleware.ts rename to express-pg-jwt/src/middleware/map-error.middleware.ts diff --git a/assets/express/example-app/src/middleware/validate-jwt.middleware.test.ts b/express-pg-jwt/src/middleware/validate-jwt.middleware.test.ts similarity index 100% rename from assets/express/example-app/src/middleware/validate-jwt.middleware.test.ts rename to express-pg-jwt/src/middleware/validate-jwt.middleware.test.ts diff --git a/assets/express/example-app/src/middleware/validate-jwt.middleware.ts b/express-pg-jwt/src/middleware/validate-jwt.middleware.ts similarity index 100% rename from assets/express/example-app/src/middleware/validate-jwt.middleware.ts rename to express-pg-jwt/src/middleware/validate-jwt.middleware.ts diff --git a/assets/express/example-app/src/middleware/validate-request.middleware.test.ts b/express-pg-jwt/src/middleware/validate-request.middleware.test.ts similarity index 100% rename from assets/express/example-app/src/middleware/validate-request.middleware.test.ts rename to express-pg-jwt/src/middleware/validate-request.middleware.test.ts diff --git a/assets/express/example-app/src/middleware/validate-request.middleware.ts b/express-pg-jwt/src/middleware/validate-request.middleware.ts similarity index 100% rename from assets/express/example-app/src/middleware/validate-request.middleware.ts rename to express-pg-jwt/src/middleware/validate-request.middleware.ts diff --git a/assets/express/example-app/src/utils/api/handler.test.ts b/express-pg-jwt/src/utils/api/handler.test.ts similarity index 100% rename from assets/express/example-app/src/utils/api/handler.test.ts rename to express-pg-jwt/src/utils/api/handler.test.ts diff --git a/assets/express/example-app/src/utils/api/handler.ts b/express-pg-jwt/src/utils/api/handler.ts similarity index 100% rename from assets/express/example-app/src/utils/api/handler.ts rename to express-pg-jwt/src/utils/api/handler.ts diff --git a/assets/express/example-app/src/utils/api/index.ts b/express-pg-jwt/src/utils/api/index.ts similarity index 100% rename from assets/express/example-app/src/utils/api/index.ts rename to express-pg-jwt/src/utils/api/index.ts diff --git a/assets/express/example-app/src/utils/api/response.test.ts b/express-pg-jwt/src/utils/api/response.test.ts similarity index 100% rename from assets/express/example-app/src/utils/api/response.test.ts rename to express-pg-jwt/src/utils/api/response.test.ts diff --git a/assets/express/example-app/src/utils/api/response.ts b/express-pg-jwt/src/utils/api/response.ts similarity index 100% rename from assets/express/example-app/src/utils/api/response.ts rename to express-pg-jwt/src/utils/api/response.ts diff --git a/assets/express/example-app/src/utils/api/route.test.ts b/express-pg-jwt/src/utils/api/route.test.ts similarity index 100% rename from assets/express/example-app/src/utils/api/route.test.ts rename to express-pg-jwt/src/utils/api/route.test.ts diff --git a/assets/express/example-app/src/utils/api/route.ts b/express-pg-jwt/src/utils/api/route.ts similarity index 100% rename from assets/express/example-app/src/utils/api/route.ts rename to express-pg-jwt/src/utils/api/route.ts diff --git a/assets/express/example-app/src/utils/environment.test.ts b/express-pg-jwt/src/utils/environment.test.ts similarity index 100% rename from assets/express/example-app/src/utils/environment.test.ts rename to express-pg-jwt/src/utils/environment.test.ts diff --git a/assets/express/example-app/src/utils/environment.ts b/express-pg-jwt/src/utils/environment.ts similarity index 100% rename from assets/express/example-app/src/utils/environment.ts rename to express-pg-jwt/src/utils/environment.ts diff --git a/assets/express/example-app/src/utils/error.test.ts b/express-pg-jwt/src/utils/error.test.ts similarity index 100% rename from assets/express/example-app/src/utils/error.test.ts rename to express-pg-jwt/src/utils/error.test.ts diff --git a/assets/express/example-app/src/utils/error.ts b/express-pg-jwt/src/utils/error.ts similarity index 100% rename from assets/express/example-app/src/utils/error.ts rename to express-pg-jwt/src/utils/error.ts diff --git a/assets/express/example-app/src/utils/openapi.test.ts b/express-pg-jwt/src/utils/openapi.test.ts similarity index 100% rename from assets/express/example-app/src/utils/openapi.test.ts rename to express-pg-jwt/src/utils/openapi.test.ts diff --git a/assets/express/example-app/src/utils/openapi.ts b/express-pg-jwt/src/utils/openapi.ts similarity index 100% rename from assets/express/example-app/src/utils/openapi.ts rename to express-pg-jwt/src/utils/openapi.ts diff --git a/assets/express/example-app/src/utils/query/error.test.ts b/express-pg-jwt/src/utils/query/error.test.ts similarity index 100% rename from assets/express/example-app/src/utils/query/error.test.ts rename to express-pg-jwt/src/utils/query/error.test.ts diff --git a/assets/express/example-app/src/utils/query/error.ts b/express-pg-jwt/src/utils/query/error.ts similarity index 100% rename from assets/express/example-app/src/utils/query/error.ts rename to express-pg-jwt/src/utils/query/error.ts diff --git a/assets/express/example-app/src/utils/query/filtration.ts b/express-pg-jwt/src/utils/query/filtration.ts similarity index 100% rename from assets/express/example-app/src/utils/query/filtration.ts rename to express-pg-jwt/src/utils/query/filtration.ts diff --git a/assets/express/example-app/src/utils/query/index.ts b/express-pg-jwt/src/utils/query/index.ts similarity index 100% rename from assets/express/example-app/src/utils/query/index.ts rename to express-pg-jwt/src/utils/query/index.ts diff --git a/assets/express/example-app/src/utils/query/pagination.test.ts b/express-pg-jwt/src/utils/query/pagination.test.ts similarity index 100% rename from assets/express/example-app/src/utils/query/pagination.test.ts rename to express-pg-jwt/src/utils/query/pagination.test.ts diff --git a/assets/express/example-app/src/utils/query/pagination.ts b/express-pg-jwt/src/utils/query/pagination.ts similarity index 100% rename from assets/express/example-app/src/utils/query/pagination.ts rename to express-pg-jwt/src/utils/query/pagination.ts diff --git a/assets/express/example-app/src/utils/query/result.test.ts b/express-pg-jwt/src/utils/query/result.test.ts similarity index 100% rename from assets/express/example-app/src/utils/query/result.test.ts rename to express-pg-jwt/src/utils/query/result.test.ts diff --git a/assets/express/example-app/src/utils/query/result.ts b/express-pg-jwt/src/utils/query/result.ts similarity index 100% rename from assets/express/example-app/src/utils/query/result.ts rename to express-pg-jwt/src/utils/query/result.ts diff --git a/assets/express/example-app/src/utils/query/sorting.ts b/express-pg-jwt/src/utils/query/sorting.ts similarity index 100% rename from assets/express/example-app/src/utils/query/sorting.ts rename to express-pg-jwt/src/utils/query/sorting.ts diff --git a/assets/express/example-app/src/utils/types.ts b/express-pg-jwt/src/utils/types.ts similarity index 100% rename from assets/express/example-app/src/utils/types.ts rename to express-pg-jwt/src/utils/types.ts diff --git a/assets/express/example-app/src/utils/validation/attribute-sets.ts b/express-pg-jwt/src/utils/validation/attribute-sets.ts similarity index 100% rename from assets/express/example-app/src/utils/validation/attribute-sets.ts rename to express-pg-jwt/src/utils/validation/attribute-sets.ts diff --git a/assets/express/example-app/src/utils/validation/attributes.ts b/express-pg-jwt/src/utils/validation/attributes.ts similarity index 100% rename from assets/express/example-app/src/utils/validation/attributes.ts rename to express-pg-jwt/src/utils/validation/attributes.ts diff --git a/assets/express/example-app/src/utils/validation/index.ts b/express-pg-jwt/src/utils/validation/index.ts similarity index 100% rename from assets/express/example-app/src/utils/validation/index.ts rename to express-pg-jwt/src/utils/validation/index.ts diff --git a/assets/express/example-app/test/auth/login.spec-e2e.ts b/express-pg-jwt/test/auth/login.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/auth/login.spec-e2e.ts rename to express-pg-jwt/test/auth/login.spec-e2e.ts diff --git a/assets/express/example-app/test/auth/register.spec-e2e.ts b/express-pg-jwt/test/auth/register.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/auth/register.spec-e2e.ts rename to express-pg-jwt/test/auth/register.spec-e2e.ts diff --git a/assets/express/example-app/test/healthchecks/live.spec-e2e.ts b/express-pg-jwt/test/healthchecks/live.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/healthchecks/live.spec-e2e.ts rename to express-pg-jwt/test/healthchecks/live.spec-e2e.ts diff --git a/assets/express/example-app/test/healthchecks/ready.spec-e2e.ts b/express-pg-jwt/test/healthchecks/ready.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/healthchecks/ready.spec-e2e.ts rename to express-pg-jwt/test/healthchecks/ready.spec-e2e.ts diff --git a/assets/express/example-app/test/jest-e2e.config.js b/express-pg-jwt/test/jest-e2e.config.js similarity index 100% rename from assets/express/example-app/test/jest-e2e.config.js rename to express-pg-jwt/test/jest-e2e.config.js diff --git a/express-pg-jwt/test/jest.setup.ts b/express-pg-jwt/test/jest.setup.ts new file mode 100644 index 00000000..e34878f0 --- /dev/null +++ b/express-pg-jwt/test/jest.setup.ts @@ -0,0 +1,7 @@ +import '@extensions/zod/register'; +import '@extensions/knex/register'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/assets/express/example-app/test/todos/create.spec-e2e.ts b/express-pg-jwt/test/todos/create.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/todos/create.spec-e2e.ts rename to express-pg-jwt/test/todos/create.spec-e2e.ts diff --git a/assets/express/example-app/test/todos/delete.spec-e2e.ts b/express-pg-jwt/test/todos/delete.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/todos/delete.spec-e2e.ts rename to express-pg-jwt/test/todos/delete.spec-e2e.ts diff --git a/assets/express/example-app/test/todos/get.spec-e2e.ts b/express-pg-jwt/test/todos/get.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/todos/get.spec-e2e.ts rename to express-pg-jwt/test/todos/get.spec-e2e.ts diff --git a/assets/express/example-app/test/todos/list.spec-e2e.ts b/express-pg-jwt/test/todos/list.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/todos/list.spec-e2e.ts rename to express-pg-jwt/test/todos/list.spec-e2e.ts diff --git a/assets/express/example-app/test/todos/update.spec-e2e.ts b/express-pg-jwt/test/todos/update.spec-e2e.ts similarity index 100% rename from assets/express/example-app/test/todos/update.spec-e2e.ts rename to express-pg-jwt/test/todos/update.spec-e2e.ts diff --git a/assets/express/example-app/test/utils/app.ts b/express-pg-jwt/test/utils/app.ts similarity index 100% rename from assets/express/example-app/test/utils/app.ts rename to express-pg-jwt/test/utils/app.ts diff --git a/assets/express/example-app/test/utils/errors.ts b/express-pg-jwt/test/utils/errors.ts similarity index 100% rename from assets/express/example-app/test/utils/errors.ts rename to express-pg-jwt/test/utils/errors.ts diff --git a/assets/express/example-app/test/utils/expect-error.ts b/express-pg-jwt/test/utils/expect-error.ts similarity index 100% rename from assets/express/example-app/test/utils/expect-error.ts rename to express-pg-jwt/test/utils/expect-error.ts diff --git a/assets/express/example-app/test/utils/get-todo-payload.ts b/express-pg-jwt/test/utils/get-todo-payload.ts similarity index 100% rename from assets/express/example-app/test/utils/get-todo-payload.ts rename to express-pg-jwt/test/utils/get-todo-payload.ts diff --git a/assets/express/example-app/test/utils/http-exception.ts b/express-pg-jwt/test/utils/http-exception.ts similarity index 100% rename from assets/express/example-app/test/utils/http-exception.ts rename to express-pg-jwt/test/utils/http-exception.ts diff --git a/assets/express/example-app/test/utils/index.ts b/express-pg-jwt/test/utils/index.ts similarity index 100% rename from assets/express/example-app/test/utils/index.ts rename to express-pg-jwt/test/utils/index.ts diff --git a/assets/express/example-app/test/utils/sortby-field-todos.ts b/express-pg-jwt/test/utils/sortby-field-todos.ts similarity index 100% rename from assets/express/example-app/test/utils/sortby-field-todos.ts rename to express-pg-jwt/test/utils/sortby-field-todos.ts diff --git a/express-pg-jwt/tsconfig.build.json b/express-pg-jwt/tsconfig.build.json new file mode 100644 index 00000000..0feb0e16 --- /dev/null +++ b/express-pg-jwt/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": [ + "**/*spec.ts", + "**/*test.ts", + "**/*spec-e2e.ts", + "**/*test-e2e.ts", + "**/__mocks__", + ] +} diff --git a/assets/express/example-app/tsconfig.json b/express-pg-jwt/tsconfig.json similarity index 100% rename from assets/express/example-app/tsconfig.json rename to express-pg-jwt/tsconfig.json diff --git a/nest-mongo-auth0/.commitlintrc.js b/nest-mongo-auth0/.commitlintrc.js new file mode 100755 index 00000000..b0f2e40c --- /dev/null +++ b/nest-mongo-auth0/.commitlintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {}, + parserPreset: { + parserOpts: { + referenceActions: null, + issuePrefixes: ['AB#'], + }, + }, +}; diff --git a/nest-mongo-auth0/.czrc b/nest-mongo-auth0/.czrc new file mode 100644 index 00000000..64b8fecd --- /dev/null +++ b/nest-mongo-auth0/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/assets/nest/example-app/.devcontainer/devcontainer.json b/nest-mongo-auth0/.devcontainer/devcontainer.json similarity index 100% rename from assets/nest/example-app/.devcontainer/devcontainer.json rename to nest-mongo-auth0/.devcontainer/devcontainer.json diff --git a/assets/nest/example-app/.devcontainer/docker-compose.yml b/nest-mongo-auth0/.devcontainer/docker-compose.yml similarity index 100% rename from assets/nest/example-app/.devcontainer/docker-compose.yml rename to nest-mongo-auth0/.devcontainer/docker-compose.yml diff --git a/nest-mongo-auth0/.dockerignore b/nest-mongo-auth0/.dockerignore new file mode 100644 index 00000000..24da266d --- /dev/null +++ b/nest-mongo-auth0/.dockerignore @@ -0,0 +1,15 @@ +.env* +.eslint* +.git +.husky +.nvmrc +.prettier* +.vscode +coverage +coverage-e2e +dist +docker-compose* +Dockerfile +node_modules +README.md +test diff --git a/nest-mongo-auth0/.editorconfig b/nest-mongo-auth0/.editorconfig new file mode 100644 index 00000000..5e1950da --- /dev/null +++ b/nest-mongo-auth0/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/nest-mongo-auth0/.env.example b/nest-mongo-auth0/.env.example new file mode 100644 index 00000000..f9a5715e --- /dev/null +++ b/nest-mongo-auth0/.env.example @@ -0,0 +1,27 @@ +# Node +NODE_ENV=development + +# HTTP +PORT=3000 + +# Logging +ERROR_LOGGING=true +REQUEST_LOGGING=true + +# Swagger +SWAGGER=true + +# MongoDB +MONGO_PROTOCOL=mongodb +MONGO_HOST=localhost +MONGO_PORT=27017 +MONGO_USER=user +MONGO_PASSWORD=password +MONGO_DATABASE_NAME=example-app + +# AUTH0 +AUTH0_ISSUER_URL=https://mock.auth0.com/api/v2/ +AUTH0_CLIENT_ID=CLIENT_ID +AUTH0_AUDIENCE=AUDIENCE +AUTH0_CLIENT_SECRET=CLIENT_SECRET + diff --git a/nest-mongo-auth0/.eslintignore b/nest-mongo-auth0/.eslintignore new file mode 100755 index 00000000..0589ba50 --- /dev/null +++ b/nest-mongo-auth0/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.vscode +coverage +dist/ +*.config.js +*rc.js +src/extensions/**/*.js +licenses-allowed.js diff --git a/assets/nest/example-app-mongodb/.eslintrc.js b/nest-mongo-auth0/.eslintrc.js similarity index 100% rename from assets/nest/example-app-mongodb/.eslintrc.js rename to nest-mongo-auth0/.eslintrc.js diff --git a/nest-mongo-auth0/.github/workflows/audit.yaml b/nest-mongo-auth0/.github/workflows/audit.yaml new file mode 100644 index 00000000..d631240e --- /dev/null +++ b/nest-mongo-auth0/.github/workflows/audit.yaml @@ -0,0 +1,16 @@ +name: audit + +on: + pull_request: + branches: + - main + +jobs: + audit: + name: Audit packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run audit + run: npm audit --omit=dev diff --git a/nest-mongo-auth0/.github/workflows/coverage-e2e.yaml b/nest-mongo-auth0/.github/workflows/coverage-e2e.yaml new file mode 100644 index 00000000..599f71aa --- /dev/null +++ b/nest-mongo-auth0/.github/workflows/coverage-e2e.yaml @@ -0,0 +1,42 @@ +name: coverage-e2e + +on: + pull_request: + branches: + - main + +env: + NODE_ENV: test + DEBUG: '' + PORT: 3000 + PGHOST: localhost + PGPORT: 5432 + PGUSER: user + PGPASSWORD: password + PGDATABASE: tests + JWT_SECRET: super-secret + JWT_EXPIRATION: 7200 + +jobs: + coverage-e2e: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Create database + uses: isbang/compose-action@v1.4.1 + - name: Run coverage + uses: ArtiomTr/jest-coverage-report-action@v2 + with: + test-script: npm run test:e2e diff --git a/nest-mongo-auth0/.github/workflows/coverage.yaml b/nest-mongo-auth0/.github/workflows/coverage.yaml new file mode 100644 index 00000000..8de35d91 --- /dev/null +++ b/nest-mongo-auth0/.github/workflows/coverage.yaml @@ -0,0 +1,26 @@ +name: coverage + +on: + pull_request: + branches: + - main + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Get test coverage + uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/nest-mongo-auth0/.gitignore b/nest-mongo-auth0/.gitignore new file mode 100644 index 00000000..008b70cd --- /dev/null +++ b/nest-mongo-auth0/.gitignore @@ -0,0 +1,156 @@ +# This is a slightly modified version of https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# OS +.DS_Store + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage/ +coverage-e2e/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional prettier cache +.prettiercache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +# .env +.env* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Docker compose +docker-compose.override.yml + +# 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 diff --git a/nest-mongo-auth0/.husky/.gitignore b/nest-mongo-auth0/.husky/.gitignore new file mode 100644 index 00000000..c9cdc63b --- /dev/null +++ b/nest-mongo-auth0/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/nest-mongo-auth0/.husky/commit-msg b/nest-mongo-auth0/.husky/commit-msg new file mode 100755 index 00000000..9e8dfba1 --- /dev/null +++ b/nest-mongo-auth0/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit $1 diff --git a/nest-mongo-auth0/.husky/pre-commit b/nest-mongo-auth0/.husky/pre-commit new file mode 100755 index 00000000..2151bed3 --- /dev/null +++ b/nest-mongo-auth0/.husky/pre-commit @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- tsc --noEmit +npx --no -- lint-staged +npx --no -- ls-lint +npm run lint:dockerfile +npm run license:check +npm run test:cov +npm run test:e2e:cov +scripts/detect-secrets.sh + +# Enable branch naming validation by uncommenting the lines below and adjust valid_branch_regex to your preference +# # Enforce branch naming conventions +# LC_ALL=C + +# # Git branch will return an empty line before the initial commit +# git_branch=$(git branch) + +# if [ -z "$git_branch" ]; then +# echo "Initial commit, no branch name linting" +# exit 0 +# fi + +# # INSERT A REGEX PATTERN ACCORDING TO YOUR PROJECT BRANCH NAMING CONVENTION. +# branch_regex="^(chore|feat|fix|docs|style|refactor|test|revert|ci)\/[a-z0-9._-]+$" + +# # Get the branch name +# branch="$(git rev-parse --abbrev-ref HEAD)" + +# # UPDATE THE EXAMPLES TO MATCH YOUR PROJECT BRANCH NAMING CONVENTION. +# error_message="Branch name validation failed. Branch name must adhere to: /$branch_regex/. +# You should rename your branch to a valid name and try again. Here are some examples of valid branch names: + +# feat/my-feature +# test/my-feature +# fix/that-bug +# chore/my-task +# docs/update-readme +# refactor/do-thing-differently +# ci/update-build-script +# " + + +# # Use grep to do the matching, as `if [[ ]]` is not available in `sh` +# match=$(echo $branch | grep -E "$branch_regex") || echo "" + +# # Check if the match is a non-empty string +# if [ -z "$match" ]; then +# echo "$error_message" +# exit 1 +# fi diff --git a/nest-mongo-auth0/.husky/pre-push b/nest-mongo-auth0/.husky/pre-push new file mode 100755 index 00000000..b9c1cb2f --- /dev/null +++ b/nest-mongo-auth0/.husky/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" diff --git a/nest-mongo-auth0/.lintstagedrc b/nest-mongo-auth0/.lintstagedrc new file mode 100644 index 00000000..22e14328 --- /dev/null +++ b/nest-mongo-auth0/.lintstagedrc @@ -0,0 +1,12 @@ +{ + "*.{js,cjs,mjs,ts}": [ + "prettier --write", + "eslint --fix" + ], + "*.md": [ + "prettier --write", + "markdownlint --fix" + ], + "*.sh": "shellcheck -x", + "package.json": "sort-package-json" +} \ No newline at end of file diff --git a/assets/nest/example-app-mongodb/.ls-lint.yml b/nest-mongo-auth0/.ls-lint.yml similarity index 100% rename from assets/nest/example-app-mongodb/.ls-lint.yml rename to nest-mongo-auth0/.ls-lint.yml diff --git a/nest-mongo-auth0/.nvmrc b/nest-mongo-auth0/.nvmrc new file mode 100644 index 00000000..d3e33db8 --- /dev/null +++ b/nest-mongo-auth0/.nvmrc @@ -0,0 +1 @@ +v18.16 diff --git a/nest-mongo-auth0/.prettierignore b/nest-mongo-auth0/.prettierignore new file mode 100644 index 00000000..b9a7a81d --- /dev/null +++ b/nest-mongo-auth0/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +coverage +coverage-e2e +*.config.js +*rc.js diff --git a/nest-mongo-auth0/.prettierrc b/nest-mongo-auth0/.prettierrc new file mode 100644 index 00000000..dcb72794 --- /dev/null +++ b/nest-mongo-auth0/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/nest-mongo-auth0/.secrets.baseline b/nest-mongo-auth0/.secrets.baseline new file mode 100644 index 00000000..c7d1aa92 --- /dev/null +++ b/nest-mongo-auth0/.secrets.baseline @@ -0,0 +1,116 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-20T21:30:47Z" +} diff --git a/assets/nest/example-app/Dockerfile b/nest-mongo-auth0/Dockerfile similarity index 100% rename from assets/nest/example-app/Dockerfile rename to nest-mongo-auth0/Dockerfile diff --git a/nest-mongo-auth0/README.md b/nest-mongo-auth0/README.md new file mode 100644 index 00000000..cca80082 --- /dev/null +++ b/nest-mongo-auth0/README.md @@ -0,0 +1,221 @@ +# nest-mongo-auth0 + +## Setup + +### Prerequisites + +#### Node and NPM via NVM + +**Install NVM:** + +Follow the instructions [here](https://github.com/nvm-sh/nvm#installing-and-updating). + +**Install the version of node specified in the `.nvmrc` file and switch to it:** + +```bash +nvm install +nvm use +``` + +**(Optional) Set the node version as default:** + +```bash +nvm alias default $(cat .nvmrc) +``` + +**(Optional) Automatically switch Node version when changing directories:** + +Follow the instructions for your shell [here](https://github.com/nvm-sh/nvm#deeper-shell-integration). + +#### Docker + +Follow the instructions [here](https://docs.docker.com/engine/install/). + +**Post installation steps for Linux users:** + +Follow the instructions [here](https://docs.docker.com/engine/install/linux-postinstall/). + +**Docker Compose:** + +In case `docker compose` isn't setup with the docker installation follow the +instructions [here](https://docs.docker.com/compose/install/). + +### Clone the repo and cd into the folder + +```bash +git clone git@github.com:.git +cd +``` + +### Install Node modules + +```bash +npm install +``` + +### Setup environment variables + +Copy the `.env.example` file to `.env`. Update environment variables as needed. + +```bash +cp .env.example .env +``` + +### Setup environment variables for e2e tests + +Copy the `.env` file to `.env.test`. Update environment variables as needed. + +```bash +cp .env .env.test +``` + +Update `NODE_ENV` to `test`. + +Make sure to provide a different database name from what you have in `.env` +as e2e tests wipe the database clean before they run. + + +### Setup docker-compose overrides + +Create a `docker-compose.override.yml` file using the example: + +```bash +cp docker-compose.override.example.yml docker-compose.override.yml +``` + + +Open the `docker-compose.override.yml` file to adjust the database volume path. + +### Provision the services + +In order to provision the services run: + +```bash +docker-compose up -d +``` + + +**Note:** +Make sure to change your database host variable in `.env` and `.env.test` files +to your docker database service name that is in `docker-compose.yml` (e.g. `db`) + +### Dev Containers + +You can work inside a Docker container instead of your host if you like. + +**How to use (VS Code):** + +- Make sure you have installed [Dev Containers Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- Open the project's folder in VS Code +- Hit `Ctrl`/`Cmd` + `Shift` + `P` -> Dev Containers: Open Folder in Container + +For more information on Dev Containers and [supported editors](https://containers.dev/supporting) +you can check the [documentation](https://containers.dev/). + + +## Running the app + +```bash +# build the app +npm run build +# run in development mode +npm run start + +# run in watch mode +npm run start:dev + +# run in debug mode +npm run start:debug +``` + +## Test + +### Unit Tests + +```bash +# run +npm run test + +# watch +npm run test:watch + +# coverage +npm run test:cov +``` + +### End-to-end Tests + +```bash +# e2e tests +npm run test:e2e + +# e2e coverage +npm run test:e2e:cov +``` + + +## Working with Docker + +```bash +# build the image +npm run image:build + +# run the image +npm run image:run +``` + + +## Working with OpenAPI + +Swagger Document is being generated when the application is started. +In order to see it you can navigate to `{HOST}:{PORT}/swagger` +e.g. `http://localhost:3000/swagger` (make sure the app is running). + +You can change the route if you want to in `main.ts` + +## Working with Licenses + +Whenever a new `npm` module is added to the project we need to check its +license. If the license is included in the +[OSI approved license list](https://opensource.org/licenses/), then we +can use it. If not - we need to review it manually. Start by running +the following `npm` script: + +```bash +npm run license:check +``` + +If the script succeeds, that means that the license is either a included +in the OSI list, or it was already reviewed and passed the review. When +the script fails it will print the first license that needs a review. +In order to list all licenses that need a review run the following: + +```bash +npm run license:for-review +``` + +In order to verify that a license suits your case, you can use a website like +[SOOS](https://app.soos.io/research/licenses). Find the license and inspect +the `Usage` and `Requirements` sections. You need to verify that the license +is `permissive`. Look for things like whether the license allows for +`Commercial Use` and `Private Use`. + +If the package passes the review, then include it in the list in +`licenses-reviewed.js` located in the root of ths project. + +If the package does not pass the review, then look for an alternative package. + +Lastly, if you just want to print a summary of all license, run: + +```bash +npm run license:summary +``` + +## Debug + +### VS Code + +Go to the Debug menu (CTRL+SHIFT+D). From `RUN AND DEBUG` at the top select +`Run Script: Launch via NPM`. You should now be able to start debugging +by pressing `F5`. + diff --git a/assets/nest/example-app-mongodb/docker-compose.override.example.yml b/nest-mongo-auth0/docker-compose.override.example.yml similarity index 100% rename from assets/nest/example-app-mongodb/docker-compose.override.example.yml rename to nest-mongo-auth0/docker-compose.override.example.yml diff --git a/assets/nest/example-app-mongodb/docker-compose.yml b/nest-mongo-auth0/docker-compose.yml similarity index 100% rename from assets/nest/example-app-mongodb/docker-compose.yml rename to nest-mongo-auth0/docker-compose.yml diff --git a/assets/nest/example-app-mongodb/jest.config.js b/nest-mongo-auth0/jest.config.js similarity index 85% rename from assets/nest/example-app-mongodb/jest.config.js rename to nest-mongo-auth0/jest.config.js index 500e81b0..9bdd7b7a 100644 --- a/assets/nest/example-app-mongodb/jest.config.js +++ b/nest-mongo-auth0/jest.config.js @@ -2,9 +2,7 @@ * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - collectCoverageFrom: [ - '/src/**/*.(t|j)s' - ], + collectCoverageFrom: ['/src/**/*.(t|j)s'], coverageDirectory: './coverage', coveragePathIgnorePatterns: [ '/src/main.ts$', @@ -22,19 +20,15 @@ module.exports = { branches: 85, functions: 85, lines: 85, - statements: 85 - } + statements: 85, + }, }, - moduleFileExtensions: [ - 'js', - 'json', - 'ts' - ], + moduleFileExtensions: ['js', 'json', 'ts'], rootDir: '.', testEnvironment: 'node', testRegex: '.*\\.spec\\.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest' + '^.+\\.(t|j)s$': 'ts-jest', }, // path aliases from tsconfig.json moduleNameMapper: { @@ -45,5 +39,5 @@ module.exports = { '^@middleware$': '/src/middleware', '^@utils/(.*)$': '/src/utils/$1', '^@test/(.*)$': '/test/$1', - } -} + }, +}; diff --git a/nest-mongo-auth0/jest.setup.ts b/nest-mongo-auth0/jest.setup.ts new file mode 100644 index 00000000..fe68937d --- /dev/null +++ b/nest-mongo-auth0/jest.setup.ts @@ -0,0 +1,4 @@ +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/nest-mongo-auth0/licenses-allowed.js b/nest-mongo-auth0/licenses-allowed.js new file mode 100644 index 00000000..2bee46b1 --- /dev/null +++ b/nest-mongo-auth0/licenses-allowed.js @@ -0,0 +1,11 @@ +const spdxLicenses = require('spdx-license-list'); + +const separator = process.argv[2]; + +const osiApprovedLicenses = Object.entries(spdxLicenses) + .filter(([, value]) => value.osiApproved) + .map(([k]) => k); + +const reviewedLicenses = require('./licenses-reviewed'); + +console.log(osiApprovedLicenses.concat(reviewedLicenses).join(separator)); diff --git a/nest-mongo-auth0/licenses-reviewed.js b/nest-mongo-auth0/licenses-reviewed.js new file mode 100644 index 00000000..e7844022 --- /dev/null +++ b/nest-mongo-auth0/licenses-reviewed.js @@ -0,0 +1,34 @@ +module.exports = [ + /** + * @link https://creativecommons.org/licenses/by/1.0/ + * + * Dependencies: + * spdx-license-ids + * spdx-license-list + */ + 'CC0-1.0', + + /** + * @link https://creativecommons.org/licenses/by/3.0/ + * + * Dependencies: + * spdx-ranges + */ + 'CC-BY-3.0', + + /** + * @link https://creativecommons.org/licenses/by/4.0/ + * + * Dependencies: + * caniuse-lite + */ + 'CC-BY-4.0', + + /** + * @link https://blueoakcouncil.org/license/1.0.0 + * + * Dependencies: + * markdownlint-cli + */ + 'BlueOak-1.0.0', +]; diff --git a/assets/nest/example-app-mongodb/migrations/latest.ts b/nest-mongo-auth0/migrations/latest.ts similarity index 100% rename from assets/nest/example-app-mongodb/migrations/latest.ts rename to nest-mongo-auth0/migrations/latest.ts diff --git a/assets/nest/example-app-mongodb/migrations/rollback.ts b/nest-mongo-auth0/migrations/rollback.ts similarity index 100% rename from assets/nest/example-app-mongodb/migrations/rollback.ts rename to nest-mongo-auth0/migrations/rollback.ts diff --git a/assets/nest/example-app-mongodb/nest-cli.json b/nest-mongo-auth0/nest-cli.json similarity index 100% rename from assets/nest/example-app-mongodb/nest-cli.json rename to nest-mongo-auth0/nest-cli.json diff --git a/nest-mongo-auth0/package.json b/nest-mongo-auth0/package.json new file mode 100644 index 00000000..9ee77429 --- /dev/null +++ b/nest-mongo-auth0/package.json @@ -0,0 +1,98 @@ +{ + "name": "nest-mongo-auth0", + "version": "0.0.1", + "private": true, + "description": "", + "license": "UNLICENSED", + "author": "", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "image:build": "DOCKER_BUILDKIT=1 docker build -t nest-mongo-auth0 .", + "image:run": "docker run --rm --net host -e NODE_ENV=production --env-file .env nest-mongo-auth0", + "license:check": "license-checker --summary --excludePrivatePackages --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --excludePrivatePackages --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "lint:dockerfile": "docker run --rm -i hadolint/hadolint < Dockerfile", + "lint:markdown": "markdownlint **/*.md --ignore node_modules", + "prepare": "husky install", + "start": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start", + "start:debug": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --debug --watch", + "start:dev": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --watch", + "start:prod": "node dist/main", + "test": "jest", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "npx jest --config ./test/jest-e2e.config.js --runInBand", + "test:e2e:cov": "npm run test:e2e -- --coverage", + "test:watch": "jest --watch" + }, + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/axios": "^3.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/passport": "^10.0.1", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "jwks-rsa": "^3.0.1", + "mongodb": "^6.3.0", + "passport-jwt": "^4.0.1", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/node": "18.16.12", + "@types/passport-jwt": "^3.0.9", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" + } +} \ No newline at end of file diff --git a/nest-mongo-auth0/scripts/detect-secrets.sh b/nest-mongo-auth0/scripts/detect-secrets.sh new file mode 100755 index 00000000..27d54428 --- /dev/null +++ b/nest-mongo-auth0/scripts/detect-secrets.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +RED=$'\e[0;31m' + +echo "[INFO] Running detect-secrets hook" + +# Exit if docker is not found +if ! command -v docker &> /dev/null +then + echo "${RED}[ERROR] docker could not be found! Docker is required in order to run detect-secrets" + exit 1 +fi + +echo "[INFO] If you encounter any issues with detect-secrets, please refer to https://github.com/Yelp/detect-secrets" + +VOLUME_PATH="--volume $(pwd):/usr/src/app" +EXCLUDED_FILES="--exclude-files .*\.(test|spec|e2e)(-e2e|-spec)?\.(js|ts)$ --exclude-files .github/workflows/coverage-e2e.yaml" +STAGED_FILES_PATHS=$(git diff --staged --name-only -z | xargs -0) + +# shellcheck disable=SC2086 +if ! docker run --rm --name detect-secrets $VOLUME_PATH lirantal/detect-secrets $EXCLUDED_FILES --baseline .secrets.baseline $STAGED_FILES_PATHS; +then + echo "${RED}[ERROR] Remove secrets before commiting." + exit 1 +fi + +echo "[INFO] detect-secrets hook completed succesfully. No secrets found!" \ No newline at end of file diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.controller.ts b/nest-mongo-auth0/src/api/auth/auth.controller.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.controller.ts rename to nest-mongo-auth0/src/api/auth/auth.controller.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.module.ts b/nest-mongo-auth0/src/api/auth/auth.module.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.module.ts rename to nest-mongo-auth0/src/api/auth/auth.module.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/credentials.dto.ts b/nest-mongo-auth0/src/api/auth/dto/credentials.dto.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/credentials.dto.ts rename to nest-mongo-auth0/src/api/auth/dto/credentials.dto.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/index.ts b/nest-mongo-auth0/src/api/auth/dto/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/index.ts rename to nest-mongo-auth0/src/api/auth/dto/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.spec.ts b/nest-mongo-auth0/src/api/auth/guards/auth.guard.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.spec.ts rename to nest-mongo-auth0/src/api/auth/guards/auth.guard.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.ts b/nest-mongo-auth0/src/api/auth/guards/auth.guard.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.ts rename to nest-mongo-auth0/src/api/auth/guards/auth.guard.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/index.ts b/nest-mongo-auth0/src/api/auth/guards/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/index.ts rename to nest-mongo-auth0/src/api/auth/guards/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/auth.ts b/nest-mongo-auth0/src/api/auth/interfaces/auth.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/auth.ts rename to nest-mongo-auth0/src/api/auth/interfaces/auth.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/index.ts b/nest-mongo-auth0/src/api/auth/interfaces/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/index.ts rename to nest-mongo-auth0/src/api/auth/interfaces/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.spec.ts b/nest-mongo-auth0/src/api/auth/services/auth.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.spec.ts rename to nest-mongo-auth0/src/api/auth/services/auth.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.ts b/nest-mongo-auth0/src/api/auth/services/auth.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.ts rename to nest-mongo-auth0/src/api/auth/services/auth.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.spec.ts b/nest-mongo-auth0/src/api/auth/services/auth0.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.spec.ts rename to nest-mongo-auth0/src/api/auth/services/auth0.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.ts b/nest-mongo-auth0/src/api/auth/services/auth0.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.ts rename to nest-mongo-auth0/src/api/auth/services/auth0.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/index.ts b/nest-mongo-auth0/src/api/auth/services/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/index.ts rename to nest-mongo-auth0/src/api/auth/services/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.spec.ts b/nest-mongo-auth0/src/api/auth/services/jwt.strategy.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.spec.ts rename to nest-mongo-auth0/src/api/auth/services/jwt.strategy.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.ts b/nest-mongo-auth0/src/api/auth/services/jwt.strategy.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.ts rename to nest-mongo-auth0/src/api/auth/services/jwt.strategy.ts diff --git a/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.controller.ts b/nest-mongo-auth0/src/api/healthchecks/healthchecks.controller.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.controller.ts rename to nest-mongo-auth0/src/api/healthchecks/healthchecks.controller.ts diff --git a/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.module.ts b/nest-mongo-auth0/src/api/healthchecks/healthchecks.module.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.module.ts rename to nest-mongo-auth0/src/api/healthchecks/healthchecks.module.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/__mocks__/index.ts b/nest-mongo-auth0/src/api/todos/__mocks__/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/__mocks__/index.ts rename to nest-mongo-auth0/src/api/todos/__mocks__/index.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/__mocks__/todos.mocks.ts b/nest-mongo-auth0/src/api/todos/__mocks__/todos.mocks.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/__mocks__/todos.mocks.ts rename to nest-mongo-auth0/src/api/todos/__mocks__/todos.mocks.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/dto/create-todo.dto.ts b/nest-mongo-auth0/src/api/todos/dto/create-todo.dto.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/dto/create-todo.dto.ts rename to nest-mongo-auth0/src/api/todos/dto/create-todo.dto.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/dto/find-all-todos-query.dto.ts b/nest-mongo-auth0/src/api/todos/dto/find-all-todos-query.dto.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/dto/find-all-todos-query.dto.ts rename to nest-mongo-auth0/src/api/todos/dto/find-all-todos-query.dto.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/dto/index.ts b/nest-mongo-auth0/src/api/todos/dto/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/dto/index.ts rename to nest-mongo-auth0/src/api/todos/dto/index.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/dto/update-todo.dto.ts b/nest-mongo-auth0/src/api/todos/dto/update-todo.dto.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/dto/update-todo.dto.ts rename to nest-mongo-auth0/src/api/todos/dto/update-todo.dto.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/entities/index.ts b/nest-mongo-auth0/src/api/todos/entities/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/entities/index.ts rename to nest-mongo-auth0/src/api/todos/entities/index.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/entities/todo.entity.ts b/nest-mongo-auth0/src/api/todos/entities/todo.entity.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/entities/todo.entity.ts rename to nest-mongo-auth0/src/api/todos/entities/todo.entity.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/error-mappings/index.ts b/nest-mongo-auth0/src/api/todos/error-mappings/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/error-mappings/index.ts rename to nest-mongo-auth0/src/api/todos/error-mappings/index.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/nest-mongo-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts rename to nest-mongo-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/interfaces/index.ts b/nest-mongo-auth0/src/api/todos/interfaces/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/interfaces/index.ts rename to nest-mongo-auth0/src/api/todos/interfaces/index.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/interfaces/todos.interface.ts b/nest-mongo-auth0/src/api/todos/interfaces/todos.interface.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/interfaces/todos.interface.ts rename to nest-mongo-auth0/src/api/todos/interfaces/todos.interface.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/repositories/index.ts b/nest-mongo-auth0/src/api/todos/repositories/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/repositories/index.ts rename to nest-mongo-auth0/src/api/todos/repositories/index.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.spec.ts b/nest-mongo-auth0/src/api/todos/repositories/todos.repository.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.spec.ts rename to nest-mongo-auth0/src/api/todos/repositories/todos.repository.spec.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.ts b/nest-mongo-auth0/src/api/todos/repositories/todos.repository.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.ts rename to nest-mongo-auth0/src/api/todos/repositories/todos.repository.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/todos.controller.ts b/nest-mongo-auth0/src/api/todos/todos.controller.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/todos.controller.ts rename to nest-mongo-auth0/src/api/todos/todos.controller.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/todos.module.ts b/nest-mongo-auth0/src/api/todos/todos.module.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/todos.module.ts rename to nest-mongo-auth0/src/api/todos/todos.module.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/todos.service.spec.ts b/nest-mongo-auth0/src/api/todos/todos.service.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/todos.service.spec.ts rename to nest-mongo-auth0/src/api/todos/todos.service.spec.ts diff --git a/assets/nest/example-app-mongodb/src/api/todos/todos.service.ts b/nest-mongo-auth0/src/api/todos/todos.service.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/api/todos/todos.service.ts rename to nest-mongo-auth0/src/api/todos/todos.service.ts diff --git a/assets/nest/example-app-pg/src/api/users/entities/index.ts b/nest-mongo-auth0/src/api/users/entities/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/entities/index.ts rename to nest-mongo-auth0/src/api/users/entities/index.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/user.entity.ts b/nest-mongo-auth0/src/api/users/entities/user.entity.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/entities/user.entity.ts rename to nest-mongo-auth0/src/api/users/entities/user.entity.ts diff --git a/assets/nest/example-app-pg/src/api/users/error-mappings/index.ts b/nest-mongo-auth0/src/api/users/error-mappings/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/error-mappings/index.ts rename to nest-mongo-auth0/src/api/users/error-mappings/index.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/user-email-taken.error-mapping.ts b/nest-mongo-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/user-email-taken.error-mapping.ts rename to nest-mongo-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts diff --git a/assets/nest/example-app-pg/src/api/users/repositories/index.ts b/nest-mongo-auth0/src/api/users/repositories/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/repositories/index.ts rename to nest-mongo-auth0/src/api/users/repositories/index.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.spec.ts b/nest-mongo-auth0/src/api/users/repositories/users.repository.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.spec.ts rename to nest-mongo-auth0/src/api/users/repositories/users.repository.spec.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.ts b/nest-mongo-auth0/src/api/users/repositories/users.repository.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.ts rename to nest-mongo-auth0/src/api/users/repositories/users.repository.ts diff --git a/assets/nest/example-app-pg/src/api/users/users.module.ts b/nest-mongo-auth0/src/api/users/users.module.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/users.module.ts rename to nest-mongo-auth0/src/api/users/users.module.ts diff --git a/assets/nest/example-app-mongodb/src/app.module.ts b/nest-mongo-auth0/src/app.module.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/app.module.ts rename to nest-mongo-auth0/src/app.module.ts diff --git a/assets/nest/example-app-mongodb/src/database/base-repository.repository.spec.ts b/nest-mongo-auth0/src/database/base-repository.repository.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/base-repository.repository.spec.ts rename to nest-mongo-auth0/src/database/base-repository.repository.spec.ts diff --git a/assets/nest/example-app-mongodb/src/database/base-repository.repository.ts b/nest-mongo-auth0/src/database/base-repository.repository.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/base-repository.repository.ts rename to nest-mongo-auth0/src/database/base-repository.repository.ts diff --git a/assets/nest/example-app-mongodb/src/database/constants.ts b/nest-mongo-auth0/src/database/constants.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/constants.ts rename to nest-mongo-auth0/src/database/constants.ts diff --git a/assets/nest/example-app-mongodb/src/database/database.module.ts b/nest-mongo-auth0/src/database/database.module.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/database.module.ts rename to nest-mongo-auth0/src/database/database.module.ts diff --git a/assets/nest/example-app-mongodb/src/database/database.service.ts b/nest-mongo-auth0/src/database/database.service.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/database.service.ts rename to nest-mongo-auth0/src/database/database.service.ts diff --git a/assets/nest/example-app-mongodb/src/database/error-mappings/index.ts b/nest-mongo-auth0/src/database/error-mappings/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/error-mappings/index.ts rename to nest-mongo-auth0/src/database/error-mappings/index.ts diff --git a/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.spec.ts b/nest-mongo-auth0/src/database/error-mappings/record-not-found.error-mapping.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.spec.ts rename to nest-mongo-auth0/src/database/error-mappings/record-not-found.error-mapping.spec.ts diff --git a/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.ts b/nest-mongo-auth0/src/database/error-mappings/record-not-found.error-mapping.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.ts rename to nest-mongo-auth0/src/database/error-mappings/record-not-found.error-mapping.ts diff --git a/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.spec.ts b/nest-mongo-auth0/src/database/error-mappings/unique-violation.error-mapping.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.spec.ts rename to nest-mongo-auth0/src/database/error-mappings/unique-violation.error-mapping.spec.ts diff --git a/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.ts b/nest-mongo-auth0/src/database/error-mappings/unique-violation.error-mapping.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.ts rename to nest-mongo-auth0/src/database/error-mappings/unique-violation.error-mapping.ts diff --git a/assets/nest/example-app-mongodb/src/database/errors/duplicate-record.error.ts b/nest-mongo-auth0/src/database/errors/duplicate-record.error.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/errors/duplicate-record.error.ts rename to nest-mongo-auth0/src/database/errors/duplicate-record.error.ts diff --git a/assets/nest/example-app-mongodb/src/database/errors/index.ts b/nest-mongo-auth0/src/database/errors/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/errors/index.ts rename to nest-mongo-auth0/src/database/errors/index.ts diff --git a/assets/nest/example-app-mongodb/src/database/errors/record-not-found.error.ts b/nest-mongo-auth0/src/database/errors/record-not-found.error.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/errors/record-not-found.error.ts rename to nest-mongo-auth0/src/database/errors/record-not-found.error.ts diff --git a/assets/nest/example-app-mongodb/src/database/utils/error.ts b/nest-mongo-auth0/src/database/utils/error.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/utils/error.ts rename to nest-mongo-auth0/src/database/utils/error.ts diff --git a/assets/nest/example-app-mongodb/src/database/utils/index.ts b/nest-mongo-auth0/src/database/utils/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/database/utils/index.ts rename to nest-mongo-auth0/src/database/utils/index.ts diff --git a/assets/nest/example-app-mongodb/src/main.ts b/nest-mongo-auth0/src/main.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/main.ts rename to nest-mongo-auth0/src/main.ts diff --git a/assets/nest/example-app-mongodb/src/utils/api/index.ts b/nest-mongo-auth0/src/utils/api/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/api/index.ts rename to nest-mongo-auth0/src/utils/api/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/api/response.ts b/nest-mongo-auth0/src/utils/api/response.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/api/response.ts rename to nest-mongo-auth0/src/utils/api/response.ts diff --git a/assets/nest/example-app-mongodb/src/utils/class-transformers/index.ts b/nest-mongo-auth0/src/utils/class-transformers/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/class-transformers/index.ts rename to nest-mongo-auth0/src/utils/class-transformers/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/class-transformers/lower-case.ts b/nest-mongo-auth0/src/utils/class-transformers/lower-case.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/class-transformers/lower-case.ts rename to nest-mongo-auth0/src/utils/class-transformers/lower-case.ts diff --git a/assets/nest/example-app-mongodb/src/utils/class-transformers/object-id.ts b/nest-mongo-auth0/src/utils/class-transformers/object-id.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/class-transformers/object-id.ts rename to nest-mongo-auth0/src/utils/class-transformers/object-id.ts diff --git a/assets/nest/example-app-mongodb/src/utils/class-transformers/trim.ts b/nest-mongo-auth0/src/utils/class-transformers/trim.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/class-transformers/trim.ts rename to nest-mongo-auth0/src/utils/class-transformers/trim.ts diff --git a/assets/nest/example-app-mongodb/src/utils/decorators/index.ts b/nest-mongo-auth0/src/utils/decorators/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/decorators/index.ts rename to nest-mongo-auth0/src/utils/decorators/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.spec.ts b/nest-mongo-auth0/src/utils/decorators/public.decorator.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.spec.ts rename to nest-mongo-auth0/src/utils/decorators/public.decorator.spec.ts diff --git a/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.ts b/nest-mongo-auth0/src/utils/decorators/public.decorator.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.ts rename to nest-mongo-auth0/src/utils/decorators/public.decorator.ts diff --git a/assets/nest/example-app-mongodb/src/utils/dtos/errors.dto.ts b/nest-mongo-auth0/src/utils/dtos/errors.dto.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/dtos/errors.dto.ts rename to nest-mongo-auth0/src/utils/dtos/errors.dto.ts diff --git a/assets/nest/example-app-mongodb/src/utils/dtos/index.ts b/nest-mongo-auth0/src/utils/dtos/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/dtos/index.ts rename to nest-mongo-auth0/src/utils/dtos/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/dtos/pagination.dto.ts b/nest-mongo-auth0/src/utils/dtos/pagination.dto.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/dtos/pagination.dto.ts rename to nest-mongo-auth0/src/utils/dtos/pagination.dto.ts diff --git a/assets/nest/example-app-mongodb/src/utils/entities/generic-entity.ts b/nest-mongo-auth0/src/utils/entities/generic-entity.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/entities/generic-entity.ts rename to nest-mongo-auth0/src/utils/entities/generic-entity.ts diff --git a/assets/nest/example-app-mongodb/src/utils/entities/index.ts b/nest-mongo-auth0/src/utils/entities/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/entities/index.ts rename to nest-mongo-auth0/src/utils/entities/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/enums/errors.enum.ts b/nest-mongo-auth0/src/utils/enums/errors.enum.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/enums/errors.enum.ts rename to nest-mongo-auth0/src/utils/enums/errors.enum.ts diff --git a/assets/nest/example-app-mongodb/src/utils/enums/index.ts b/nest-mongo-auth0/src/utils/enums/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/enums/index.ts rename to nest-mongo-auth0/src/utils/enums/index.ts diff --git a/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.spec.ts b/nest-mongo-auth0/src/utils/environment.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.spec.ts rename to nest-mongo-auth0/src/utils/environment.spec.ts diff --git a/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.ts b/nest-mongo-auth0/src/utils/environment.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.ts rename to nest-mongo-auth0/src/utils/environment.ts diff --git a/assets/nest/example-app-mongodb/src/utils/error-logging.filter.spec.ts b/nest-mongo-auth0/src/utils/error-logging.filter.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/error-logging.filter.spec.ts rename to nest-mongo-auth0/src/utils/error-logging.filter.spec.ts diff --git a/assets/nest/example-app-mongodb/src/utils/error-logging.filter.ts b/nest-mongo-auth0/src/utils/error-logging.filter.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/error-logging.filter.ts rename to nest-mongo-auth0/src/utils/error-logging.filter.ts diff --git a/assets/nest/example-app-mongodb/src/utils/error.spec.ts b/nest-mongo-auth0/src/utils/error.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/error.spec.ts rename to nest-mongo-auth0/src/utils/error.spec.ts diff --git a/assets/nest/example-app-mongodb/src/utils/error.ts b/nest-mongo-auth0/src/utils/error.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/error.ts rename to nest-mongo-auth0/src/utils/error.ts diff --git a/assets/nest/example-app-mongodb/src/utils/interceptors/index.ts b/nest-mongo-auth0/src/utils/interceptors/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/interceptors/index.ts rename to nest-mongo-auth0/src/utils/interceptors/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/interceptors/request-logging.interceptor.ts b/nest-mongo-auth0/src/utils/interceptors/request-logging.interceptor.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/interceptors/request-logging.interceptor.ts rename to nest-mongo-auth0/src/utils/interceptors/request-logging.interceptor.ts diff --git a/assets/nest/example-app-mongodb/src/utils/interceptors/service-to-http-error.interceptor.ts b/nest-mongo-auth0/src/utils/interceptors/service-to-http-error.interceptor.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/interceptors/service-to-http-error.interceptor.ts rename to nest-mongo-auth0/src/utils/interceptors/service-to-http-error.interceptor.ts diff --git a/assets/nest/example-app-mongodb/src/utils/interfaces/index.ts b/nest-mongo-auth0/src/utils/interfaces/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/interfaces/index.ts rename to nest-mongo-auth0/src/utils/interfaces/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/pipes/index.ts b/nest-mongo-auth0/src/utils/pipes/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/pipes/index.ts rename to nest-mongo-auth0/src/utils/pipes/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.spec.ts b/nest-mongo-auth0/src/utils/pipes/to-object-id.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.spec.ts rename to nest-mongo-auth0/src/utils/pipes/to-object-id.spec.ts diff --git a/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.ts b/nest-mongo-auth0/src/utils/pipes/to-object-id.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.ts rename to nest-mongo-auth0/src/utils/pipes/to-object-id.ts diff --git a/assets/nest/example-app-mongodb/src/utils/query/error.spec.ts b/nest-mongo-auth0/src/utils/query/error.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/query/error.spec.ts rename to nest-mongo-auth0/src/utils/query/error.spec.ts diff --git a/assets/nest/example-app-mongodb/src/utils/query/error.ts b/nest-mongo-auth0/src/utils/query/error.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/query/error.ts rename to nest-mongo-auth0/src/utils/query/error.ts diff --git a/assets/nest/example-app-mongodb/src/utils/query/index.ts b/nest-mongo-auth0/src/utils/query/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/query/index.ts rename to nest-mongo-auth0/src/utils/query/index.ts diff --git a/assets/nest/example-app-mongodb/src/utils/query/pagination.spec.ts b/nest-mongo-auth0/src/utils/query/pagination.spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/query/pagination.spec.ts rename to nest-mongo-auth0/src/utils/query/pagination.spec.ts diff --git a/assets/nest/example-app-mongodb/src/utils/query/pagination.ts b/nest-mongo-auth0/src/utils/query/pagination.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/query/pagination.ts rename to nest-mongo-auth0/src/utils/query/pagination.ts diff --git a/assets/nest/example-app-mongodb/src/utils/query/sorting.ts b/nest-mongo-auth0/src/utils/query/sorting.ts similarity index 100% rename from assets/nest/example-app-mongodb/src/utils/query/sorting.ts rename to nest-mongo-auth0/src/utils/query/sorting.ts diff --git a/assets/nest/example-app-mongodb/test/healthchecks.e2e-spec.ts b/nest-mongo-auth0/test/healthchecks.e2e-spec.ts similarity index 100% rename from assets/nest/example-app-mongodb/test/healthchecks.e2e-spec.ts rename to nest-mongo-auth0/test/healthchecks.e2e-spec.ts diff --git a/assets/nest/example-app-mongodb/test/jest-e2e.config.js b/nest-mongo-auth0/test/jest-e2e.config.js similarity index 82% rename from assets/nest/example-app-mongodb/test/jest-e2e.config.js rename to nest-mongo-auth0/test/jest-e2e.config.js index 498320e9..21834cad 100644 --- a/assets/nest/example-app-mongodb/test/jest-e2e.config.js +++ b/nest-mongo-auth0/test/jest-e2e.config.js @@ -2,21 +2,15 @@ * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - moduleFileExtensions: [ - 'js', - 'json', - 'ts' - ], + moduleFileExtensions: ['js', 'json', 'ts'], rootDir: '..', testEnvironment: 'node', testRegex: '.e2e-spec.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest' + '^.+\\.(t|j)s$': 'ts-jest', }, coverageDirectory: 'coverage-e2e', - collectCoverageFrom: [ - '/src/api/**/*.(t|j)s' - ], + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], coveragePathIgnorePatterns: [ '/src/main.ts$', '/.*spec.ts$', @@ -32,9 +26,10 @@ module.exports = { branches: 85, functions: 85, lines: 85, - statements: 85 - } + statements: 85, + }, }, + setupFiles: ['/jest.setup.ts'], // path aliases from tsconfig.json moduleNameMapper: { '^@api/(.*)$': '/src/api/$1', @@ -45,4 +40,4 @@ module.exports = { '^@utils/(.*)$': '/src/utils/$1', '^@test/(.*)$': '/test/$1', }, -} +}; diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/create.e2e-spec.ts b/nest-mongo-auth0/test/todos/create.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/create.e2e-spec.ts rename to nest-mongo-auth0/test/todos/create.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-all.e2e-spec.ts b/nest-mongo-auth0/test/todos/find-all.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-all.e2e-spec.ts rename to nest-mongo-auth0/test/todos/find-all.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-one.e2e-spec.ts b/nest-mongo-auth0/test/todos/find-one.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-one.e2e-spec.ts rename to nest-mongo-auth0/test/todos/find-one.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/remove.e2e-spec.ts b/nest-mongo-auth0/test/todos/remove.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/remove.e2e-spec.ts rename to nest-mongo-auth0/test/todos/remove.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/update.e2e-spec.ts b/nest-mongo-auth0/test/todos/update.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/update.e2e-spec.ts rename to nest-mongo-auth0/test/todos/update.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/get-todo-payload.ts b/nest-mongo-auth0/test/todos/utils/get-todo-payload.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/get-todo-payload.ts rename to nest-mongo-auth0/test/todos/utils/get-todo-payload.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/index.ts b/nest-mongo-auth0/test/todos/utils/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/index.ts rename to nest-mongo-auth0/test/todos/utils/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/sortby-field-todos.ts b/nest-mongo-auth0/test/todos/utils/sortby-field-todos.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/sortby-field-todos.ts rename to nest-mongo-auth0/test/todos/utils/sortby-field-todos.ts diff --git a/assets/nest/example-app-mongodb/test/utils/db-setup.ts b/nest-mongo-auth0/test/utils/db-setup.ts similarity index 100% rename from assets/nest/example-app-mongodb/test/utils/db-setup.ts rename to nest-mongo-auth0/test/utils/db-setup.ts diff --git a/assets/nest/example-app-mongodb/test/utils/expect-error.ts b/nest-mongo-auth0/test/utils/expect-error.ts similarity index 100% rename from assets/nest/example-app-mongodb/test/utils/expect-error.ts rename to nest-mongo-auth0/test/utils/expect-error.ts diff --git a/assets/nest/example-app-mongodb/test/utils/index.ts b/nest-mongo-auth0/test/utils/index.ts similarity index 100% rename from assets/nest/example-app-mongodb/test/utils/index.ts rename to nest-mongo-auth0/test/utils/index.ts diff --git a/assets/nest/example-app-mongodb/tsconfig.build.json b/nest-mongo-auth0/tsconfig.build.json similarity index 100% rename from assets/nest/example-app-mongodb/tsconfig.build.json rename to nest-mongo-auth0/tsconfig.build.json diff --git a/assets/nest/example-app-mongodb/tsconfig.json b/nest-mongo-auth0/tsconfig.json similarity index 100% rename from assets/nest/example-app-mongodb/tsconfig.json rename to nest-mongo-auth0/tsconfig.json diff --git a/nest-mongo-jwt/.commitlintrc.js b/nest-mongo-jwt/.commitlintrc.js new file mode 100755 index 00000000..b0f2e40c --- /dev/null +++ b/nest-mongo-jwt/.commitlintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {}, + parserPreset: { + parserOpts: { + referenceActions: null, + issuePrefixes: ['AB#'], + }, + }, +}; diff --git a/nest-mongo-jwt/.czrc b/nest-mongo-jwt/.czrc new file mode 100644 index 00000000..64b8fecd --- /dev/null +++ b/nest-mongo-jwt/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/nest-mongo-jwt/.devcontainer/devcontainer.json b/nest-mongo-jwt/.devcontainer/devcontainer.json new file mode 100644 index 00000000..3ff38d20 --- /dev/null +++ b/nest-mongo-jwt/.devcontainer/devcontainer.json @@ -0,0 +1,9 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Nest API DevContainer", + "dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"], + "service": "api", + "workspaceFolder": "/usr/src/app", + "remoteUser": "root" +} diff --git a/nest-mongo-jwt/.devcontainer/docker-compose.yml b/nest-mongo-jwt/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..b01c619a --- /dev/null +++ b/nest-mongo-jwt/.devcontainer/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + api: + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" diff --git a/nest-mongo-jwt/.dockerignore b/nest-mongo-jwt/.dockerignore new file mode 100644 index 00000000..24da266d --- /dev/null +++ b/nest-mongo-jwt/.dockerignore @@ -0,0 +1,15 @@ +.env* +.eslint* +.git +.husky +.nvmrc +.prettier* +.vscode +coverage +coverage-e2e +dist +docker-compose* +Dockerfile +node_modules +README.md +test diff --git a/nest-mongo-jwt/.editorconfig b/nest-mongo-jwt/.editorconfig new file mode 100644 index 00000000..5e1950da --- /dev/null +++ b/nest-mongo-jwt/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/nest-mongo-jwt/.env.example b/nest-mongo-jwt/.env.example new file mode 100644 index 00000000..bad61563 --- /dev/null +++ b/nest-mongo-jwt/.env.example @@ -0,0 +1,25 @@ +# Node +NODE_ENV=development + +# HTTP +PORT=3000 + +# Logging +ERROR_LOGGING=true +REQUEST_LOGGING=true + +# Swagger +SWAGGER=true + +# MongoDB +MONGO_PROTOCOL=mongodb +MONGO_HOST=localhost +MONGO_PORT=27017 +MONGO_USER=user +MONGO_PASSWORD=password +MONGO_DATABASE_NAME=example-app + +# JWT +JWT_SECRET=super-secret +JWT_EXPIRATION=7200 + diff --git a/nest-mongo-jwt/.eslintignore b/nest-mongo-jwt/.eslintignore new file mode 100755 index 00000000..0589ba50 --- /dev/null +++ b/nest-mongo-jwt/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.vscode +coverage +dist/ +*.config.js +*rc.js +src/extensions/**/*.js +licenses-allowed.js diff --git a/assets/nest/example-app-pg/.eslintrc.js b/nest-mongo-jwt/.eslintrc.js similarity index 100% rename from assets/nest/example-app-pg/.eslintrc.js rename to nest-mongo-jwt/.eslintrc.js diff --git a/nest-mongo-jwt/.github/workflows/audit.yaml b/nest-mongo-jwt/.github/workflows/audit.yaml new file mode 100644 index 00000000..d631240e --- /dev/null +++ b/nest-mongo-jwt/.github/workflows/audit.yaml @@ -0,0 +1,16 @@ +name: audit + +on: + pull_request: + branches: + - main + +jobs: + audit: + name: Audit packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run audit + run: npm audit --omit=dev diff --git a/nest-mongo-jwt/.github/workflows/coverage-e2e.yaml b/nest-mongo-jwt/.github/workflows/coverage-e2e.yaml new file mode 100644 index 00000000..599f71aa --- /dev/null +++ b/nest-mongo-jwt/.github/workflows/coverage-e2e.yaml @@ -0,0 +1,42 @@ +name: coverage-e2e + +on: + pull_request: + branches: + - main + +env: + NODE_ENV: test + DEBUG: '' + PORT: 3000 + PGHOST: localhost + PGPORT: 5432 + PGUSER: user + PGPASSWORD: password + PGDATABASE: tests + JWT_SECRET: super-secret + JWT_EXPIRATION: 7200 + +jobs: + coverage-e2e: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Create database + uses: isbang/compose-action@v1.4.1 + - name: Run coverage + uses: ArtiomTr/jest-coverage-report-action@v2 + with: + test-script: npm run test:e2e diff --git a/nest-mongo-jwt/.github/workflows/coverage.yaml b/nest-mongo-jwt/.github/workflows/coverage.yaml new file mode 100644 index 00000000..8de35d91 --- /dev/null +++ b/nest-mongo-jwt/.github/workflows/coverage.yaml @@ -0,0 +1,26 @@ +name: coverage + +on: + pull_request: + branches: + - main + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Get test coverage + uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/nest-mongo-jwt/.gitignore b/nest-mongo-jwt/.gitignore new file mode 100644 index 00000000..008b70cd --- /dev/null +++ b/nest-mongo-jwt/.gitignore @@ -0,0 +1,156 @@ +# This is a slightly modified version of https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# OS +.DS_Store + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage/ +coverage-e2e/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional prettier cache +.prettiercache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +# .env +.env* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Docker compose +docker-compose.override.yml + +# 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 diff --git a/nest-mongo-jwt/.husky/.gitignore b/nest-mongo-jwt/.husky/.gitignore new file mode 100644 index 00000000..c9cdc63b --- /dev/null +++ b/nest-mongo-jwt/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/nest-mongo-jwt/.husky/commit-msg b/nest-mongo-jwt/.husky/commit-msg new file mode 100755 index 00000000..9e8dfba1 --- /dev/null +++ b/nest-mongo-jwt/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit $1 diff --git a/nest-mongo-jwt/.husky/pre-commit b/nest-mongo-jwt/.husky/pre-commit new file mode 100755 index 00000000..2151bed3 --- /dev/null +++ b/nest-mongo-jwt/.husky/pre-commit @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- tsc --noEmit +npx --no -- lint-staged +npx --no -- ls-lint +npm run lint:dockerfile +npm run license:check +npm run test:cov +npm run test:e2e:cov +scripts/detect-secrets.sh + +# Enable branch naming validation by uncommenting the lines below and adjust valid_branch_regex to your preference +# # Enforce branch naming conventions +# LC_ALL=C + +# # Git branch will return an empty line before the initial commit +# git_branch=$(git branch) + +# if [ -z "$git_branch" ]; then +# echo "Initial commit, no branch name linting" +# exit 0 +# fi + +# # INSERT A REGEX PATTERN ACCORDING TO YOUR PROJECT BRANCH NAMING CONVENTION. +# branch_regex="^(chore|feat|fix|docs|style|refactor|test|revert|ci)\/[a-z0-9._-]+$" + +# # Get the branch name +# branch="$(git rev-parse --abbrev-ref HEAD)" + +# # UPDATE THE EXAMPLES TO MATCH YOUR PROJECT BRANCH NAMING CONVENTION. +# error_message="Branch name validation failed. Branch name must adhere to: /$branch_regex/. +# You should rename your branch to a valid name and try again. Here are some examples of valid branch names: + +# feat/my-feature +# test/my-feature +# fix/that-bug +# chore/my-task +# docs/update-readme +# refactor/do-thing-differently +# ci/update-build-script +# " + + +# # Use grep to do the matching, as `if [[ ]]` is not available in `sh` +# match=$(echo $branch | grep -E "$branch_regex") || echo "" + +# # Check if the match is a non-empty string +# if [ -z "$match" ]; then +# echo "$error_message" +# exit 1 +# fi diff --git a/nest-mongo-jwt/.husky/pre-push b/nest-mongo-jwt/.husky/pre-push new file mode 100755 index 00000000..b9c1cb2f --- /dev/null +++ b/nest-mongo-jwt/.husky/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" diff --git a/nest-mongo-jwt/.lintstagedrc b/nest-mongo-jwt/.lintstagedrc new file mode 100644 index 00000000..22e14328 --- /dev/null +++ b/nest-mongo-jwt/.lintstagedrc @@ -0,0 +1,12 @@ +{ + "*.{js,cjs,mjs,ts}": [ + "prettier --write", + "eslint --fix" + ], + "*.md": [ + "prettier --write", + "markdownlint --fix" + ], + "*.sh": "shellcheck -x", + "package.json": "sort-package-json" +} \ No newline at end of file diff --git a/assets/nest/example-app-pg/.ls-lint.yml b/nest-mongo-jwt/.ls-lint.yml similarity index 100% rename from assets/nest/example-app-pg/.ls-lint.yml rename to nest-mongo-jwt/.ls-lint.yml diff --git a/nest-mongo-jwt/.nvmrc b/nest-mongo-jwt/.nvmrc new file mode 100644 index 00000000..d3e33db8 --- /dev/null +++ b/nest-mongo-jwt/.nvmrc @@ -0,0 +1 @@ +v18.16 diff --git a/nest-mongo-jwt/.prettierignore b/nest-mongo-jwt/.prettierignore new file mode 100644 index 00000000..b9a7a81d --- /dev/null +++ b/nest-mongo-jwt/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +coverage +coverage-e2e +*.config.js +*rc.js diff --git a/nest-mongo-jwt/.prettierrc b/nest-mongo-jwt/.prettierrc new file mode 100644 index 00000000..dcb72794 --- /dev/null +++ b/nest-mongo-jwt/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/nest-mongo-jwt/.secrets.baseline b/nest-mongo-jwt/.secrets.baseline new file mode 100644 index 00000000..e84e83f8 --- /dev/null +++ b/nest-mongo-jwt/.secrets.baseline @@ -0,0 +1,116 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-20T21:30:59Z" +} diff --git a/nest-mongo-jwt/Dockerfile b/nest-mongo-jwt/Dockerfile new file mode 100644 index 00000000..759bf918 --- /dev/null +++ b/nest-mongo-jwt/Dockerfile @@ -0,0 +1,89 @@ +# +# 🧑‍💻 Development +# +FROM node:18-alpine AS dev + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Set the working directory +WORKDIR /usr/src/app + +# Set to dev environment +ENV NODE_ENV development + +# Copy the source code +COPY --chown=node:node . . + +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # rebuild bcrypt + && npm rebuild bcrypt + +# +# 🏡 Production Build +# +FROM node:18-alpine as build + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# In order to run `npm run build` we need access to the Nest CLI. +# Nest CLI is a dev dependency. +COPY --chown=node:node --from=dev /usr/src/app/node_modules ./node_modules +# Copy source code +COPY --chown=node:node . . + +# Generate the production build. The build script runs "nest build" to compile the application. +RUN npm run build + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # skip dev dependencies + --omit=dev \ + # rebuild bcrypt + && npm rebuild bcrypt \ + # attempt to reduce image size + && npm cache clean --force + +# Set Docker as a non-root user +USER node + +# +# 🚀 Production Server +# +FROM node:18-alpine as prod + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# Copy only the necessary files +COPY --chown=node:node --from=build /usr/src/app/dist dist +COPY --chown=node:node --from=build /usr/src/app/node_modules node_modules + +# Set Docker as non-root user +USER node + +# Run +CMD [ "node", "./dist/src/main.js" ] \ No newline at end of file diff --git a/nest-mongo-jwt/README.md b/nest-mongo-jwt/README.md new file mode 100644 index 00000000..5afc4536 --- /dev/null +++ b/nest-mongo-jwt/README.md @@ -0,0 +1,221 @@ +# nest-mongo-jwt + +## Setup + +### Prerequisites + +#### Node and NPM via NVM + +**Install NVM:** + +Follow the instructions [here](https://github.com/nvm-sh/nvm#installing-and-updating). + +**Install the version of node specified in the `.nvmrc` file and switch to it:** + +```bash +nvm install +nvm use +``` + +**(Optional) Set the node version as default:** + +```bash +nvm alias default $(cat .nvmrc) +``` + +**(Optional) Automatically switch Node version when changing directories:** + +Follow the instructions for your shell [here](https://github.com/nvm-sh/nvm#deeper-shell-integration). + +#### Docker + +Follow the instructions [here](https://docs.docker.com/engine/install/). + +**Post installation steps for Linux users:** + +Follow the instructions [here](https://docs.docker.com/engine/install/linux-postinstall/). + +**Docker Compose:** + +In case `docker compose` isn't setup with the docker installation follow the +instructions [here](https://docs.docker.com/compose/install/). + +### Clone the repo and cd into the folder + +```bash +git clone git@github.com:.git +cd +``` + +### Install Node modules + +```bash +npm install +``` + +### Setup environment variables + +Copy the `.env.example` file to `.env`. Update environment variables as needed. + +```bash +cp .env.example .env +``` + +### Setup environment variables for e2e tests + +Copy the `.env` file to `.env.test`. Update environment variables as needed. + +```bash +cp .env .env.test +``` + +Update `NODE_ENV` to `test`. + +Make sure to provide a different database name from what you have in `.env` +as e2e tests wipe the database clean before they run. + + +### Setup docker-compose overrides + +Create a `docker-compose.override.yml` file using the example: + +```bash +cp docker-compose.override.example.yml docker-compose.override.yml +``` + + +Open the `docker-compose.override.yml` file to adjust the database volume path. + +### Provision the services + +In order to provision the services run: + +```bash +docker-compose up -d +``` + + +**Note:** +Make sure to change your database host variable in `.env` and `.env.test` files +to your docker database service name that is in `docker-compose.yml` (e.g. `db`) + +### Dev Containers + +You can work inside a Docker container instead of your host if you like. + +**How to use (VS Code):** + +- Make sure you have installed [Dev Containers Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- Open the project's folder in VS Code +- Hit `Ctrl`/`Cmd` + `Shift` + `P` -> Dev Containers: Open Folder in Container + +For more information on Dev Containers and [supported editors](https://containers.dev/supporting) +you can check the [documentation](https://containers.dev/). + + +## Running the app + +```bash +# build the app +npm run build +# run in development mode +npm run start + +# run in watch mode +npm run start:dev + +# run in debug mode +npm run start:debug +``` + +## Test + +### Unit Tests + +```bash +# run +npm run test + +# watch +npm run test:watch + +# coverage +npm run test:cov +``` + +### End-to-end Tests + +```bash +# e2e tests +npm run test:e2e + +# e2e coverage +npm run test:e2e:cov +``` + + +## Working with Docker + +```bash +# build the image +npm run image:build + +# run the image +npm run image:run +``` + + +## Working with OpenAPI + +Swagger Document is being generated when the application is started. +In order to see it you can navigate to `{HOST}:{PORT}/swagger` +e.g. `http://localhost:3000/swagger` (make sure the app is running). + +You can change the route if you want to in `main.ts` + +## Working with Licenses + +Whenever a new `npm` module is added to the project we need to check its +license. If the license is included in the +[OSI approved license list](https://opensource.org/licenses/), then we +can use it. If not - we need to review it manually. Start by running +the following `npm` script: + +```bash +npm run license:check +``` + +If the script succeeds, that means that the license is either a included +in the OSI list, or it was already reviewed and passed the review. When +the script fails it will print the first license that needs a review. +In order to list all licenses that need a review run the following: + +```bash +npm run license:for-review +``` + +In order to verify that a license suits your case, you can use a website like +[SOOS](https://app.soos.io/research/licenses). Find the license and inspect +the `Usage` and `Requirements` sections. You need to verify that the license +is `permissive`. Look for things like whether the license allows for +`Commercial Use` and `Private Use`. + +If the package passes the review, then include it in the list in +`licenses-reviewed.js` located in the root of ths project. + +If the package does not pass the review, then look for an alternative package. + +Lastly, if you just want to print a summary of all license, run: + +```bash +npm run license:summary +``` + +## Debug + +### VS Code + +Go to the Debug menu (CTRL+SHIFT+D). From `RUN AND DEBUG` at the top select +`Run Script: Launch via NPM`. You should now be able to start debugging +by pressing `F5`. + diff --git a/nest-mongo-jwt/docker-compose.override.example.yml b/nest-mongo-jwt/docker-compose.override.example.yml new file mode 100644 index 00000000..48d10555 --- /dev/null +++ b/nest-mongo-jwt/docker-compose.override.example.yml @@ -0,0 +1,6 @@ +services: + + # uncomment the lines below and adjust the volume if desired + # db: + # volumes: + # - /path/to/volume:/var/lib/mongodb/data diff --git a/nest-mongo-jwt/docker-compose.yml b/nest-mongo-jwt/docker-compose.yml new file mode 100644 index 00000000..4e8ba19a --- /dev/null +++ b/nest-mongo-jwt/docker-compose.yml @@ -0,0 +1,45 @@ +version: '3' + +services: + db: + image: mongo:latest + container_name: my-mongodb + ports: + - '27017:27017' + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD} + command: --auth + volumes: + - db-data:/var/lib/mongodb/data + + api: + build: + context: . + dockerfile: Dockerfile + target: dev + container_name: todo-api + image: todo-api-dev + ports: + - '${PORT}:${PORT}' + env_file: + - .env + # Mount host directory to docker container to support watch mode + volumes: + - .:/usr/src/app + # This ensures that the NestJS container manages the node_modules folder + # rather than synchronizes it with the host machine + - api-node_modules:/usr/src/app/node_modules + depends_on: + - db + command: > + sh -c "cd /usr/src/app + && npm run start:dev" + +volumes: + api-node_modules: + db-data: + +networks: + default: + name: nest-api diff --git a/assets/nest/example-app-pg/jest.config.js b/nest-mongo-jwt/jest.config.js similarity index 81% rename from assets/nest/example-app-pg/jest.config.js rename to nest-mongo-jwt/jest.config.js index 778e5a2c..9bdd7b7a 100644 --- a/assets/nest/example-app-pg/jest.config.js +++ b/nest-mongo-jwt/jest.config.js @@ -2,9 +2,7 @@ * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - collectCoverageFrom: [ - '/src/**/*.(t|j)s' - ], + collectCoverageFrom: ['/src/**/*.(t|j)s'], coverageDirectory: './coverage', coveragePathIgnorePatterns: [ '/src/main.ts$', @@ -22,23 +20,16 @@ module.exports = { branches: 85, functions: 85, lines: 85, - statements: 85 - } + statements: 85, + }, }, - moduleFileExtensions: [ - 'js', - 'json', - 'ts' - ], + moduleFileExtensions: ['js', 'json', 'ts'], rootDir: '.', testEnvironment: 'node', testRegex: '.*\\.spec\\.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest' + '^.+\\.(t|j)s$': 'ts-jest', }, - setupFiles: [ - '/jest.setup.ts' - ], // path aliases from tsconfig.json moduleNameMapper: { '^@api/(.*)$': '/src/api/$1', @@ -47,5 +38,6 @@ module.exports = { '^@extensions/(.*)$': '/src/extensions/$1', '^@middleware$': '/src/middleware', '^@utils/(.*)$': '/src/utils/$1', - } -} + '^@test/(.*)$': '/test/$1', + }, +}; diff --git a/nest-mongo-jwt/jest.setup.ts b/nest-mongo-jwt/jest.setup.ts new file mode 100644 index 00000000..fe68937d --- /dev/null +++ b/nest-mongo-jwt/jest.setup.ts @@ -0,0 +1,4 @@ +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/nest-mongo-jwt/licenses-allowed.js b/nest-mongo-jwt/licenses-allowed.js new file mode 100644 index 00000000..2bee46b1 --- /dev/null +++ b/nest-mongo-jwt/licenses-allowed.js @@ -0,0 +1,11 @@ +const spdxLicenses = require('spdx-license-list'); + +const separator = process.argv[2]; + +const osiApprovedLicenses = Object.entries(spdxLicenses) + .filter(([, value]) => value.osiApproved) + .map(([k]) => k); + +const reviewedLicenses = require('./licenses-reviewed'); + +console.log(osiApprovedLicenses.concat(reviewedLicenses).join(separator)); diff --git a/nest-mongo-jwt/licenses-reviewed.js b/nest-mongo-jwt/licenses-reviewed.js new file mode 100644 index 00000000..e7844022 --- /dev/null +++ b/nest-mongo-jwt/licenses-reviewed.js @@ -0,0 +1,34 @@ +module.exports = [ + /** + * @link https://creativecommons.org/licenses/by/1.0/ + * + * Dependencies: + * spdx-license-ids + * spdx-license-list + */ + 'CC0-1.0', + + /** + * @link https://creativecommons.org/licenses/by/3.0/ + * + * Dependencies: + * spdx-ranges + */ + 'CC-BY-3.0', + + /** + * @link https://creativecommons.org/licenses/by/4.0/ + * + * Dependencies: + * caniuse-lite + */ + 'CC-BY-4.0', + + /** + * @link https://blueoakcouncil.org/license/1.0.0 + * + * Dependencies: + * markdownlint-cli + */ + 'BlueOak-1.0.0', +]; diff --git a/nest-mongo-jwt/migrations/latest.ts b/nest-mongo-jwt/migrations/latest.ts new file mode 100644 index 00000000..c61f9f41 --- /dev/null +++ b/nest-mongo-jwt/migrations/latest.ts @@ -0,0 +1,8 @@ +import { Collections } from '@database/constants'; +import { Db } from 'mongodb'; + +export async function latest(mongodb: Db) { + await mongodb.createCollection(Collections.Users); + await mongodb.createCollection(Collections.Todos); + await mongodb.createIndex(Collections.Todos, 'user_id'); +} diff --git a/nest-mongo-jwt/migrations/rollback.ts b/nest-mongo-jwt/migrations/rollback.ts new file mode 100644 index 00000000..f6ef1318 --- /dev/null +++ b/nest-mongo-jwt/migrations/rollback.ts @@ -0,0 +1,7 @@ +import { Collections } from '@database/constants'; +import { Db } from 'mongodb'; + +export async function rollback(mongodb: Db) { + await mongodb.dropCollection(Collections.Todos); + await mongodb.dropCollection(Collections.Users); +} diff --git a/assets/nest/example-app-pg/nest-cli.json b/nest-mongo-jwt/nest-cli.json similarity index 100% rename from assets/nest/example-app-pg/nest-cli.json rename to nest-mongo-jwt/nest-cli.json diff --git a/nest-mongo-jwt/package.json b/nest-mongo-jwt/package.json new file mode 100644 index 00000000..ea3a263f --- /dev/null +++ b/nest-mongo-jwt/package.json @@ -0,0 +1,96 @@ +{ + "name": "nest-mongo-jwt", + "version": "0.0.1", + "private": true, + "description": "", + "license": "UNLICENSED", + "author": "", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "image:build": "DOCKER_BUILDKIT=1 docker build -t nest-mongo-jwt .", + "image:run": "docker run --rm --net host -e NODE_ENV=production --env-file .env nest-mongo-jwt", + "license:check": "license-checker --summary --excludePrivatePackages --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --excludePrivatePackages --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "lint:dockerfile": "docker run --rm -i hadolint/hadolint < Dockerfile", + "lint:markdown": "markdownlint **/*.md --ignore node_modules", + "prepare": "husky install", + "start": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start", + "start:debug": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --debug --watch", + "start:dev": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --watch", + "start:prod": "node dist/main", + "test": "jest", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "npx jest --config ./test/jest-e2e.config.js --runInBand", + "test:e2e:cov": "npm run test:e2e -- --coverage", + "test:watch": "jest --watch" + }, + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "express-jwt": "^8.4.1", + "jsonwebtoken": "^9.0.0", + "mongodb": "^6.3.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "18.16.12", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" + } +} \ No newline at end of file diff --git a/nest-mongo-jwt/scripts/detect-secrets.sh b/nest-mongo-jwt/scripts/detect-secrets.sh new file mode 100755 index 00000000..27d54428 --- /dev/null +++ b/nest-mongo-jwt/scripts/detect-secrets.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +RED=$'\e[0;31m' + +echo "[INFO] Running detect-secrets hook" + +# Exit if docker is not found +if ! command -v docker &> /dev/null +then + echo "${RED}[ERROR] docker could not be found! Docker is required in order to run detect-secrets" + exit 1 +fi + +echo "[INFO] If you encounter any issues with detect-secrets, please refer to https://github.com/Yelp/detect-secrets" + +VOLUME_PATH="--volume $(pwd):/usr/src/app" +EXCLUDED_FILES="--exclude-files .*\.(test|spec|e2e)(-e2e|-spec)?\.(js|ts)$ --exclude-files .github/workflows/coverage-e2e.yaml" +STAGED_FILES_PATHS=$(git diff --staged --name-only -z | xargs -0) + +# shellcheck disable=SC2086 +if ! docker run --rm --name detect-secrets $VOLUME_PATH lirantal/detect-secrets $EXCLUDED_FILES --baseline .secrets.baseline $STAGED_FILES_PATHS; +then + echo "${RED}[ERROR] Remove secrets before commiting." + exit 1 +fi + +echo "[INFO] detect-secrets hook completed succesfully. No secrets found!" \ No newline at end of file diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.controller.ts b/nest-mongo-jwt/src/api/auth/auth.controller.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.controller.ts rename to nest-mongo-jwt/src/api/auth/auth.controller.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.module.ts b/nest-mongo-jwt/src/api/auth/auth.module.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.module.ts rename to nest-mongo-jwt/src/api/auth/auth.module.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/credentials.dto.ts b/nest-mongo-jwt/src/api/auth/dto/credentials.dto.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/credentials.dto.ts rename to nest-mongo-jwt/src/api/auth/dto/credentials.dto.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/index.ts b/nest-mongo-jwt/src/api/auth/dto/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/index.ts rename to nest-mongo-jwt/src/api/auth/dto/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/index.ts b/nest-mongo-jwt/src/api/auth/entities/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/index.ts rename to nest-mongo-jwt/src/api/auth/entities/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/jwt-token.entity.ts b/nest-mongo-jwt/src/api/auth/entities/jwt-token.entity.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/jwt-token.entity.ts rename to nest-mongo-jwt/src/api/auth/entities/jwt-token.entity.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.spec.ts b/nest-mongo-jwt/src/api/auth/guards/auth.guard.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.spec.ts rename to nest-mongo-jwt/src/api/auth/guards/auth.guard.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.ts b/nest-mongo-jwt/src/api/auth/guards/auth.guard.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.ts rename to nest-mongo-jwt/src/api/auth/guards/auth.guard.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/index.ts b/nest-mongo-jwt/src/api/auth/guards/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/index.ts rename to nest-mongo-jwt/src/api/auth/guards/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/auth.ts b/nest-mongo-jwt/src/api/auth/interfaces/auth.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/auth.ts rename to nest-mongo-jwt/src/api/auth/interfaces/auth.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/index.ts b/nest-mongo-jwt/src/api/auth/interfaces/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/index.ts rename to nest-mongo-jwt/src/api/auth/interfaces/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.spec.ts b/nest-mongo-jwt/src/api/auth/services/auth.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.spec.ts rename to nest-mongo-jwt/src/api/auth/services/auth.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.ts b/nest-mongo-jwt/src/api/auth/services/auth.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.ts rename to nest-mongo-jwt/src/api/auth/services/auth.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/index.ts b/nest-mongo-jwt/src/api/auth/services/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/index.ts rename to nest-mongo-jwt/src/api/auth/services/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.spec.ts b/nest-mongo-jwt/src/api/auth/services/jwt.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.spec.ts rename to nest-mongo-jwt/src/api/auth/services/jwt.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.ts b/nest-mongo-jwt/src/api/auth/services/jwt.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.ts rename to nest-mongo-jwt/src/api/auth/services/jwt.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.spec.ts b/nest-mongo-jwt/src/api/auth/services/password.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.spec.ts rename to nest-mongo-jwt/src/api/auth/services/password.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.ts b/nest-mongo-jwt/src/api/auth/services/password.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.ts rename to nest-mongo-jwt/src/api/auth/services/password.service.ts diff --git a/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.controller.ts b/nest-mongo-jwt/src/api/healthchecks/healthchecks.controller.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/healthchecks/healthchecks.controller.ts rename to nest-mongo-jwt/src/api/healthchecks/healthchecks.controller.ts diff --git a/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.module.ts b/nest-mongo-jwt/src/api/healthchecks/healthchecks.module.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/healthchecks/healthchecks.module.ts rename to nest-mongo-jwt/src/api/healthchecks/healthchecks.module.ts diff --git a/assets/nest/example-app-pg/src/api/todos/__mocks__/index.ts b/nest-mongo-jwt/src/api/todos/__mocks__/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/__mocks__/index.ts rename to nest-mongo-jwt/src/api/todos/__mocks__/index.ts diff --git a/nest-mongo-jwt/src/api/todos/__mocks__/todos.mocks.ts b/nest-mongo-jwt/src/api/todos/__mocks__/todos.mocks.ts new file mode 100644 index 00000000..ccd08437 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/__mocks__/todos.mocks.ts @@ -0,0 +1,85 @@ +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { Paginated, SortOrder } from '@utils/query'; +import { Todo } from '../entities'; +import { TodosSortBy, UpdateTodoDto } from '../dto'; +import { UserData } from '@api/auth/interfaces'; +import { ObjectId } from 'mongodb'; + +export const mockedUser: UserData = { + user: { + sub: new ObjectId(100).toString(), + email: 'example@email.com', + }, +}; + +const userId = new ObjectId(mockedUser.user.sub); + +export const todo: Todo = { + _id: new ObjectId(1), + name: 'todo', + note: null, + completed: false, + createdAt: new Date('2023-09-01T09:44:15.515Z').getTime(), + updatedAt: new Date('2023-09-01T09:44:15.515Z').getTime(), + userId, +}; + +export const secondTodo: Todo = { + _id: new ObjectId(2), + name: 'second-todo', + note: null, + completed: false, + createdAt: new Date('2023-09-02:44:15.515Z').getTime(), + updatedAt: new Date('2023-09-02:44:15.515Z').getTime(), + userId, +}; + +export const createTodoInput: CreateTodoInput = { + createTodoDto: { + name: todo.name, + completed: todo.completed, + note: todo.note, + }, + userId, +}; + +export const updateTodoDtoInput: UpdateTodoDto = { + name: 'new name', + note: 'new note', + completed: true, +}; + +export const updateTodoInput: UpdateTodoInput = { + _id: todo._id, + updateTodoDto: updateTodoDtoInput, + userId, +}; + +export const findOneTodoInput: FindOneTodoInput = { + _id: todo._id, + userId, +}; + +export const findAllTodosInput: FindAllTodosInput = { + query: { + pageNumber: 1, + pageSize: 20, + column: TodosSortBy.CreatedAt, + order: SortOrder.Asc, + }, + userId, +}; + +export const getPaginatedResponse = (data: T[]): Paginated => { + return { + items: data, + total: data.length, + totalPages: 1, + currentPage: 1, + }; +}; diff --git a/assets/nest/example-app-pg/src/api/todos/dto/create-todo.dto.ts b/nest-mongo-jwt/src/api/todos/dto/create-todo.dto.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/dto/create-todo.dto.ts rename to nest-mongo-jwt/src/api/todos/dto/create-todo.dto.ts diff --git a/nest-mongo-jwt/src/api/todos/dto/find-all-todos-query.dto.ts b/nest-mongo-jwt/src/api/todos/dto/find-all-todos-query.dto.ts new file mode 100644 index 00000000..2fd6a7b8 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/dto/find-all-todos-query.dto.ts @@ -0,0 +1,75 @@ +import { SortOrder } from '@utils/query'; +import { + Allow, + IsEnum, + IsNumber, + IsOptional, + MaxLength, + MinLength, +} from 'class-validator'; +import { Transform } from 'class-transformer'; +import { Trim } from '@utils/class-transformers'; +import { ApiProperty } from '@nestjs/swagger'; + +export enum TodosSortBy { + Name = 'name', + CreatedAt = 'createdAt', +} + +export class FindAllTodosQueryDto { + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ + type: Boolean, + required: false, + }) + @Transform(({ value }) => + value === 'true' ? true : value === 'false' ? false : undefined + ) + @IsOptional() + completed?: boolean; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @IsEnum(TodosSortBy) + column?: TodosSortBy; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @IsEnum(SortOrder) + @Allow(undefined) + order?: SortOrder | undefined; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number = 1; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number = 20; +} diff --git a/assets/nest/example-app-pg/src/api/todos/dto/index.ts b/nest-mongo-jwt/src/api/todos/dto/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/dto/index.ts rename to nest-mongo-jwt/src/api/todos/dto/index.ts diff --git a/assets/nest/example-app-pg/src/api/todos/dto/update-todo.dto.ts b/nest-mongo-jwt/src/api/todos/dto/update-todo.dto.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/dto/update-todo.dto.ts rename to nest-mongo-jwt/src/api/todos/dto/update-todo.dto.ts diff --git a/assets/nest/example-app-pg/src/api/todos/entities/index.ts b/nest-mongo-jwt/src/api/todos/entities/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/entities/index.ts rename to nest-mongo-jwt/src/api/todos/entities/index.ts diff --git a/nest-mongo-jwt/src/api/todos/entities/todo.entity.ts b/nest-mongo-jwt/src/api/todos/entities/todo.entity.ts new file mode 100644 index 00000000..69be3141 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/entities/todo.entity.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ToObjectId } from '@utils/class-transformers'; +import { GenericEntity } from '@utils/entities/generic-entity'; +import { ObjectId } from 'mongodb'; + +export class Todo extends GenericEntity { + @ApiProperty() + @ToObjectId() + userId: ObjectId; + + @ApiProperty() + name: string; + + @ApiProperty({ nullable: true }) + note: string | null; + + @ApiProperty({ default: false }) + completed = false; +} diff --git a/assets/nest/example-app-pg/src/api/todos/error-mappings/index.ts b/nest-mongo-jwt/src/api/todos/error-mappings/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/error-mappings/index.ts rename to nest-mongo-jwt/src/api/todos/error-mappings/index.ts diff --git a/nest-mongo-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/nest-mongo-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts new file mode 100644 index 00000000..de7ad0d7 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts @@ -0,0 +1,3 @@ +import { recordNotFoundErrorMapping } from '@database/error-mappings'; + +export const TodoUserNotFound = recordNotFoundErrorMapping('User not found'); diff --git a/assets/nest/example-app-pg/src/api/todos/interfaces/index.ts b/nest-mongo-jwt/src/api/todos/interfaces/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/interfaces/index.ts rename to nest-mongo-jwt/src/api/todos/interfaces/index.ts diff --git a/nest-mongo-jwt/src/api/todos/interfaces/todos.interface.ts b/nest-mongo-jwt/src/api/todos/interfaces/todos.interface.ts new file mode 100644 index 00000000..312948fe --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/interfaces/todos.interface.ts @@ -0,0 +1,24 @@ +import { ObjectId } from 'mongodb'; +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from '../dto'; + +interface UserData { + userId: ObjectId; +} + +interface TodoId { + _id?: ObjectId; +} + +export interface CreateTodoInput extends UserData { + createTodoDto: CreateTodoDto; +} + +export interface FindAllTodosInput extends UserData { + query: FindAllTodosQueryDto; +} + +export interface FindOneTodoInput extends UserData, TodoId, UpdateTodoDto {} + +export interface UpdateTodoInput extends FindOneTodoInput { + updateTodoDto: UpdateTodoDto; +} diff --git a/assets/nest/example-app-pg/src/api/todos/repositories/index.ts b/nest-mongo-jwt/src/api/todos/repositories/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/repositories/index.ts rename to nest-mongo-jwt/src/api/todos/repositories/index.ts diff --git a/nest-mongo-jwt/src/api/todos/repositories/todos.repository.spec.ts b/nest-mongo-jwt/src/api/todos/repositories/todos.repository.spec.ts new file mode 100644 index 00000000..93121166 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/repositories/todos.repository.spec.ts @@ -0,0 +1,169 @@ +import { Test } from '@nestjs/testing'; +import { TodosRepository } from './todos.repository'; +import { + createTodoInput, + findAllTodosInput, + findOneTodoInput, + mockedUser, + secondTodo, + todo, + updateTodoInput, +} from '../__mocks__'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; +import { SortOrder } from '@utils/query'; + +describe('TodosRepository', () => { + let todosRepository: TodosRepository; + + const mockFn = jest.fn().mockImplementation(() => Promise.resolve([])); + + const toArray = jest.fn().mockImplementation(() => Promise.resolve([])); + const sort = jest.fn().mockImplementation(() => ({ + skip: () => ({ limit: () => ({ toArray }) }), + })); + const find = jest.fn().mockImplementation(() => ({ + sort, + })); + + const insertOne = jest.fn().mockImplementation(async () => mockFn()); + const findOne = jest.fn().mockImplementation(() => mockFn()); + const findOneAndUpdate = jest.fn().mockImplementation(() => mockFn()); + const findOneAndDelete = jest.fn().mockImplementation(() => mockFn()); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: DatabaseService, + useFactory: () => ({ + connection: { + collection: () => ({ + insertOne, + find, + findOne, + findOneAndUpdate, + findOneAndDelete, + }), + }, + }), + }, + TodosRepository, + ], + }).compile(); + + todosRepository = moduleRef.get(TodosRepository); + }); + + it('create - create a todo', async () => { + mockFn.mockImplementationOnce(() => + Promise.resolve({ insertedId: todo._id }) + ); + + const result = await todosRepository.create(createTodoInput); + + expect(result).toBe(todo._id); + expect(insertOne).toHaveBeenCalledWith( + expect.objectContaining({ + ...createTodoInput.createTodoDto, + userId: createTodoInput.userId, + }) + ); + }); + + it('findOne - find a todo', async () => { + mockFn.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOne(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(findOne).toHaveBeenCalledWith(findOneTodoInput); + }); + + it('findOneOrFail - find a todo', async () => { + mockFn.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOneOrFail(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(findOne).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('update', () => { + it('update - update a todo', async () => { + const updatedTodo = { + ...todo, + name: updateTodoInput.updateTodoDto.name, + }; + + mockFn.mockImplementationOnce(() => Promise.resolve(updatedTodo)); + + const result = await todosRepository.update(updateTodoInput); + + expect(result).toStrictEqual(updatedTodo); + expect(findOneAndUpdate).toHaveBeenCalledWith( + { + _id: updateTodoInput._id, + userId: new ObjectId(mockedUser.user.sub), + }, + { + $set: { + completed: updateTodoInput.updateTodoDto.completed, + name: updateTodoInput.updateTodoDto.name, + note: updateTodoInput.updateTodoDto.note, + }, + }, + { returnDocument: 'after' } + ); + }); + }); + + it('remove - delete a todo', async () => { + mockFn.mockImplementationOnce(() => Promise.resolve(todo)); + + const result = await todosRepository.remove(findOneTodoInput); + + expect(result).toBe(todo); + expect(findOneAndDelete).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('findAll', () => { + it('find all todos for the user sorted by `createdAt` in ascending order', async () => { + toArray.mockImplementationOnce(() => Promise.resolve([todo, secondTodo])); + jest.spyOn(todosRepository, 'count').mockResolvedValueOnce(2); + + const result = await todosRepository.findAll(findAllTodosInput); + + expect(sort).toHaveBeenCalledWith('createdAt', 1); + expect(result).toStrictEqual({ + items: [todo, secondTodo], + total: 2, + currentPage: 1, + totalPages: 1, + }); + }); + + it('find all todos for the user sorted by `createdAt` in descending order', async () => { + toArray.mockImplementationOnce(() => Promise.resolve([secondTodo, todo])); + jest.spyOn(todosRepository, 'count').mockResolvedValueOnce(2); + const { userId, query } = findAllTodosInput; + + const result = await todosRepository.findAll({ + userId, + query: { + ...query, + order: SortOrder.Desc, + }, + }); + + expect(sort).toHaveBeenCalledWith('createdAt', -1); + expect(result).toStrictEqual({ + items: [secondTodo, todo], + total: 2, + currentPage: 1, + totalPages: 1, + }); + }); + }); +}); + diff --git a/nest-mongo-jwt/src/api/todos/repositories/todos.repository.ts b/nest-mongo-jwt/src/api/todos/repositories/todos.repository.ts new file mode 100644 index 00000000..e5835960 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/repositories/todos.repository.ts @@ -0,0 +1,110 @@ +import { rethrowError } from '@utils/error'; +import { Paginated } from '@utils/query/pagination'; +import { Todo } from '../entities'; +import { Injectable } from '@nestjs/common'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { definedOrNotFound, SortOrder } from '@utils/query'; +import { TodoUserNotFound } from '../error-mappings'; +import { paginatedResponse } from '@utils/api/response'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; +import { Errors } from '@utils/enums'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; +import { NullableKeysPartial } from '@utils/interfaces'; + +@Injectable() +export class TodosRepository extends BaseRepository { + constructor(private readonly mongodb: DatabaseService) { + super(mongodb, Tables.Todos); + } + + async create(input: CreateTodoInput): Promise { + const todo = { + _id: new ObjectId(), + userId: input.userId, + ...input.createTodoDto, + // Why do we need it in the input? + completed: false, + createdAt: Date.now(), + updatedAt: Date.now(), + }; + + return this.repository() + .insertOne(todo) + .then((value) => value.insertedId) + .catch(rethrowError(TodoUserNotFound)); + } + + async findOne( + input: Partial, + ): Promise | null> { + return this.repository().findOne(input); + } + + async findOneOrFail( + input: Partial, + ): Promise> { + return this.repository() + .findOne(input) + .then(definedOrNotFound(Errors.NotFound)); + } + + async update( + input: UpdateTodoInput, + ): Promise | null> { + const { _id, userId, updateTodoDto } = input; + + return this.repository() + .findOneAndUpdate( + { _id, userId }, + { $set: updateTodoDto }, + { returnDocument: 'after' }, + ) + .catch(rethrowError(TodoUserNotFound)); + } + + async remove( + input: FindOneTodoInput, + ): Promise | null> { + return this.repository().findOneAndDelete(input); + } + + async findAll( + input: FindAllTodosInput, + ): Promise>> { + const { + userId, + query: { pageNumber, pageSize, name, completed, column, order }, + } = input; + + const pagination = { pageNumber, pageSize }; + const filters = Object.entries({ userId, name, completed }) + .filter(([, v]) => v !== undefined) + .reduce((obj: Record, [k, v]) => { + obj[k] = v; + return obj; + }, {}); + + let cursor = this.repository().find(filters); + + if (column) { + const sortOrder = order === SortOrder.Asc ? 1 : -1; + cursor = cursor.sort(column, sortOrder); + } + + const items = await cursor + .skip(((pageNumber as number) - 1) * (pageSize as number)) + .limit(pageSize as number) + .toArray(); + + const count = await this.count(); + + return paginatedResponse(items, count, pagination); + } +} diff --git a/nest-mongo-jwt/src/api/todos/todos.controller.ts b/nest-mongo-jwt/src/api/todos/todos.controller.ts new file mode 100644 index 00000000..1c5541e2 --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/todos.controller.ts @@ -0,0 +1,116 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Delete, + Req, + Put, + Query, +} from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { + ApiBadRequestResponse, + ApiBody, + ApiCreatedResponse, + ApiNotFoundResponse, + ApiOkResponse, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { Todo } from './entities'; +import { Paginated } from '@utils/query'; +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from './dto'; +import { UserData } from '@api/auth/interfaces'; +import { + BadRequestDto, + NotFoundDto, + UnprocessableEntityDto, +} from '@utils/dtos'; +import { Errors } from '@utils/enums'; +import { ObjectId } from 'mongodb'; +import { NullableKeysPartial } from '@utils/interfaces'; +import { GenericEntity } from '@utils/entities'; +import { ToObjectIdPipe } from '@utils/pipes'; + +@ApiTags('Todos') +@Controller('v1/todos') +export class TodosController { + constructor(private readonly todosService: TodosService) {} + + @ApiBody({ type: CreateTodoDto }) + @ApiCreatedResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @ApiUnprocessableEntityResponse({ + type: UnprocessableEntityDto, + description: Errors.UnprocessableEntity, + }) + @Post() + create( + @Body() createTodoDto: CreateTodoDto, + @Req() { user: { sub } }: UserData + ): Promise { + return this.todosService.create({ + createTodoDto, + userId: new ObjectId(sub), + }); + } + + @ApiOkResponse({ type: Todo, isArray: true }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @Get() + findAll( + @Req() { user: { sub } }: UserData, + @Query() query: FindAllTodosQueryDto + ): Promise>> { + return this.todosService.findAll({ userId: new ObjectId(sub), query }); + } + + @ApiOkResponse({ type: Todo }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Get(':id') + findOne( + @Param('id', ToObjectIdPipe) _id: ObjectId, + @Req() { user: { sub } }: UserData + ): Promise> { + return this.todosService.findOneOrFail({ _id, userId: new ObjectId(sub) }); + } + + @ApiBody({ type: UpdateTodoDto }) + @ApiOkResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Put(':id') + async update( + @Param('id', ToObjectIdPipe) _id: ObjectId, + @Req() { user: { sub } }: UserData, + @Body() updateTodoDto: UpdateTodoDto + ): Promise> { + return await this.todosService.update({ + _id, + userId: new ObjectId(sub), + updateTodoDto, + }); + } + + @ApiOkResponse({ type: Number }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Delete(':id') + remove( + @Param('id', ToObjectIdPipe) _id: GenericEntity['_id'], + @Req() { user: { sub } }: UserData + ): Promise { + return this.todosService.remove({ _id, userId: new ObjectId(sub) }); + } +} diff --git a/assets/nest/example-app-pg/src/api/todos/todos.module.ts b/nest-mongo-jwt/src/api/todos/todos.module.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/todos.module.ts rename to nest-mongo-jwt/src/api/todos/todos.module.ts diff --git a/nest-mongo-jwt/src/api/todos/todos.service.spec.ts b/nest-mongo-jwt/src/api/todos/todos.service.spec.ts new file mode 100644 index 00000000..a2eee84a --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/todos.service.spec.ts @@ -0,0 +1,183 @@ +import { DatabaseModule } from '@database/database.module'; +import { Test, TestingModule } from '@nestjs/testing'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { TodosRepository } from './repositories'; +import { + createTodoInput, + mockedUser, + getPaginatedResponse, + todo, + updateTodoDtoInput, +} from './__mocks__'; +import { + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { Errors } from '@utils/enums'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; +import { ConfigModule } from '@nestjs/config'; +import { ObjectId } from 'mongodb'; +import { DatabaseService } from '@database/database.service'; + +describe('TodosService', () => { + let service: TodosService; + let repository: TodosRepository; + const userId = mockedUser.user.sub; + + class DatabaseServiceMock { + public connection = {}; + public client = {}; + } + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + DatabaseModule, + ], + controllers: [TodosController], + providers: [TodosService, TodosRepository], + }) + .overrideProvider(DatabaseService) + .useClass(DatabaseServiceMock) + .compile(); + + service = module.get(TodosService); + repository = module.get(TodosRepository); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('create', () => { + it('should return created todo', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => null); + jest + .spyOn(repository, 'create') + .mockImplementationOnce(async () => todo._id); + + expect(await service.create(createTodoInput)).toStrictEqual(todo._id); + }); + + it('should throw error if todo with the same name exists', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => todo); + + await expect(service.create(createTodoInput)).rejects.toThrow( + new UnprocessableEntityException(Errors.UnprocessableEntity) + ); + }); + }); + + describe('findAll', () => { + it('should return todos', async () => { + const paginatedResponse = getPaginatedResponse([todo]); + + jest + .spyOn(repository, 'findAll') + .mockImplementationOnce(async () => paginatedResponse); + + expect( + await service.findAll({ userId: new ObjectId(userId), query: {} }) + ).toStrictEqual(paginatedResponse); + }); + }); + + describe('findOne', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.findOneOrFail({ + _id: todo._id, + userId: new ObjectId(userId), + }) + ).toStrictEqual(todo); + }); + }); + + describe('findOneOrFail', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.findOneOrFail({ + _id: todo._id, + userId: new ObjectId(userId), + }) + ).toStrictEqual(todo); + }); + }); + + describe('update', () => { + it('should update single todo', async () => { + const updatedTodo = { ...todo, ...updateTodoDtoInput }; + + jest + .spyOn(repository, 'update') + .mockImplementationOnce(async () => updatedTodo); + + expect( + await service.update({ + _id: todo._id, + userId: new ObjectId(userId), + updateTodoDto: updateTodoDtoInput, + }) + ).toStrictEqual(updatedTodo); + }); + + it('should return todo if there is no data to update', async () => { + jest + .spyOn(service, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.update({ + _id: todo._id, + userId: new ObjectId(userId), + updateTodoDto: {}, + }) + ).toStrictEqual(todo); + }); + }); + + describe('remove', () => { + it('should delete single todo', async () => { + jest.spyOn(repository, 'remove').mockImplementationOnce(async () => todo); + + expect( + await service.remove({ + _id: new ObjectId(), + userId: new ObjectId(userId), + }) + ).toBe(true); + }); + + it('should throw error if todo does not exist', async () => { + jest.spyOn(repository, 'remove').mockImplementationOnce(async () => null); + + await expect( + service.remove({ _id: new ObjectId(), userId: new ObjectId(userId) }) + ).rejects.toThrow(new NotFoundException(Errors.NotFound)); + }); + }); +}); diff --git a/nest-mongo-jwt/src/api/todos/todos.service.ts b/nest-mongo-jwt/src/api/todos/todos.service.ts new file mode 100644 index 00000000..9fb1c00c --- /dev/null +++ b/nest-mongo-jwt/src/api/todos/todos.service.ts @@ -0,0 +1,74 @@ +import { + Inject, + Injectable, + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { TodosRepository } from './repositories'; +import { Paginated, definedOrNotFound } from '@utils/query'; +import { Todo } from './entities/todo.entity'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from './interfaces'; +import { Errors } from '@utils/enums'; +import { NullableKeysPartial } from '@utils/interfaces'; +import { ObjectId } from 'mongodb'; + +@Injectable() +export class TodosService { + constructor( + @Inject(TodosRepository) + private readonly todos: TodosRepository + ) {} + + async create(input: CreateTodoInput): Promise { + const { + userId, + createTodoDto: { name }, + } = input; + + const todo = await this.todos.findOne({ userId, name }); + + if (todo) { + throw new UnprocessableEntityException(Errors.UnprocessableEntity); + } + + return this.todos.create(input); + } + + findAll( + input: FindAllTodosInput + ): Promise>> { + return this.todos.findAll(input); + } + + findOneOrFail( + input: Partial + ): Promise> { + return this.todos.findOneOrFail(input); + } + + update(input: UpdateTodoInput): Promise> { + const { _id, userId, updateTodoDto } = input; + + if (Object.keys(updateTodoDto).length === 0) { + return this.findOneOrFail({ _id, userId }); + } + + return this.todos.update(input).then(definedOrNotFound(Errors.NotFound)); + } + + async remove(input: FindOneTodoInput): Promise { + input._id = new ObjectId(input._id); + + const deletedTodo = await this.todos.remove(input); + if (!deletedTodo) { + throw new NotFoundException(Errors.NotFound); + } + + return true; + } +} diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/index.ts b/nest-mongo-jwt/src/api/users/entities/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/entities/index.ts rename to nest-mongo-jwt/src/api/users/entities/index.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/user.entity.ts b/nest-mongo-jwt/src/api/users/entities/user.entity.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/entities/user.entity.ts rename to nest-mongo-jwt/src/api/users/entities/user.entity.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/index.ts b/nest-mongo-jwt/src/api/users/error-mappings/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/index.ts rename to nest-mongo-jwt/src/api/users/error-mappings/index.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/user-email-taken.error-mapping.ts b/nest-mongo-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/user-email-taken.error-mapping.ts rename to nest-mongo-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/index.ts b/nest-mongo-jwt/src/api/users/repositories/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/index.ts rename to nest-mongo-jwt/src/api/users/repositories/index.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.spec.ts b/nest-mongo-jwt/src/api/users/repositories/users.repository.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.spec.ts rename to nest-mongo-jwt/src/api/users/repositories/users.repository.spec.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.ts b/nest-mongo-jwt/src/api/users/repositories/users.repository.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.ts rename to nest-mongo-jwt/src/api/users/repositories/users.repository.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/auth0/users.module.ts b/nest-mongo-jwt/src/api/users/users.module.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/auth0/users.module.ts rename to nest-mongo-jwt/src/api/users/users.module.ts diff --git a/assets/nest/example-app-pg/src/app.module.ts b/nest-mongo-jwt/src/app.module.ts similarity index 100% rename from assets/nest/example-app-pg/src/app.module.ts rename to nest-mongo-jwt/src/app.module.ts diff --git a/nest-mongo-jwt/src/database/base-repository.repository.spec.ts b/nest-mongo-jwt/src/database/base-repository.repository.spec.ts new file mode 100644 index 00000000..d3afa7a9 --- /dev/null +++ b/nest-mongo-jwt/src/database/base-repository.repository.spec.ts @@ -0,0 +1,40 @@ +import { Test } from '@nestjs/testing'; +import { BaseRepository } from './base-repository.repository'; +import { DatabaseService } from './database.service'; + +describe('BaseRepository', () => { + let baseRepository: BaseRepository; + + const estimatedDocumentCount = jest.fn( + (): Promise => Promise.resolve() + ); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: DatabaseService, + useFactory: () => ({ + connection: { + collection: () => ({ + estimatedDocumentCount, + }), + }, + }), + }, + BaseRepository, + ], + }).compile(); + + const databaseService = moduleRef.get(DatabaseService); + baseRepository = new BaseRepository(databaseService, 'test'); + }); + + it('count', async () => { + estimatedDocumentCount.mockResolvedValueOnce(1); + + const result = await baseRepository.count(); + + expect(result).toBe(1); + }); +}); diff --git a/nest-mongo-jwt/src/database/base-repository.repository.ts b/nest-mongo-jwt/src/database/base-repository.repository.ts new file mode 100644 index 00000000..3b4db097 --- /dev/null +++ b/nest-mongo-jwt/src/database/base-repository.repository.ts @@ -0,0 +1,23 @@ +import { NullableKeysPartial } from '@utils/interfaces'; +import { DatabaseService } from './database.service'; + +export class BaseRepository> { + private dbService; + + constructor( + mongodb: DatabaseService, + private readonly collectionName: string, + ) { + this.dbService = mongodb; + } + + repository() { + return this.dbService.connection.collection>( + this.collectionName, + ); + } + + async count(): Promise { + return await this.repository().estimatedDocumentCount(); + } +} diff --git a/nest-mongo-jwt/src/database/constants.ts b/nest-mongo-jwt/src/database/constants.ts new file mode 100644 index 00000000..de798394 --- /dev/null +++ b/nest-mongo-jwt/src/database/constants.ts @@ -0,0 +1,12 @@ +export const MONGO_CONNECTION = 'MONGO_CONNECTION'; +export const NEST_MONGO_OPTIONS = 'NEST_MONGO_OPTIONS'; + +export enum Tables { + Users = 'users', + Todos = 'todos', +} + +export enum Collections { + Users = 'users', + Todos = 'todos', +} diff --git a/nest-mongo-jwt/src/database/database.module.ts b/nest-mongo-jwt/src/database/database.module.ts new file mode 100644 index 00000000..b32d2400 --- /dev/null +++ b/nest-mongo-jwt/src/database/database.module.ts @@ -0,0 +1,43 @@ +import { Module, OnApplicationShutdown } from '@nestjs/common'; +import { ConfigModule, ConfigType } from '@nestjs/config'; + +import { NEST_MONGO_OPTIONS } from './constants'; +import { DatabaseService } from './database.service'; +import { dbConfig } from '@utils/environment'; + +@Module({ + imports: [ConfigModule], + providers: [ + DatabaseService, + { + provide: NEST_MONGO_OPTIONS, + inject: [dbConfig.KEY], + useFactory: (database: ConfigType) => { + const protocol = database.MONGO_PROTOCOL; + const host = database.MONGO_HOST; + const port = database.MONGO_PORT; + const user = database.MONGO_USER; + const password = database.MONGO_PASSWORD; + const databaseName = database.MONGO_DATABASE_NAME; + const delimiter = user && password ? ':' : ''; + const at = delimiter ? '@' : ''; + + const urlString = `${protocol}://${user}${delimiter}${password}${at}${host}:${port}`; + + return { + urlString, + databaseName, + clientOptions: {}, + }; + }, + }, + ], + exports: [DatabaseService], +}) +export class DatabaseModule implements OnApplicationShutdown { + constructor(private readonly mongodb: DatabaseService) {} + + async onApplicationShutdown(): Promise { + await this.mongodb.client.close(); + } +} diff --git a/nest-mongo-jwt/src/database/database.service.ts b/nest-mongo-jwt/src/database/database.service.ts new file mode 100644 index 00000000..1a56a52d --- /dev/null +++ b/nest-mongo-jwt/src/database/database.service.ts @@ -0,0 +1,27 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { NEST_MONGO_OPTIONS } from './constants'; +import { MongoClient, MongoClientOptions, Db } from 'mongodb'; + +@Injectable() +export class DatabaseService { + public connection: Db; + public client: MongoClient; + + constructor( + @Inject(NEST_MONGO_OPTIONS) + nestMongoOptions: { + urlString: string; + databaseName: string; + clientOptions: MongoClientOptions; + }, + ) { + const client = new MongoClient( + nestMongoOptions.urlString, + nestMongoOptions.clientOptions, + ); + const { databaseName } = nestMongoOptions; + + this.connection = client.db(databaseName); + this.client = client; + } +} diff --git a/nest-mongo-jwt/src/database/error-mappings/index.ts b/nest-mongo-jwt/src/database/error-mappings/index.ts new file mode 100644 index 00000000..297377f0 --- /dev/null +++ b/nest-mongo-jwt/src/database/error-mappings/index.ts @@ -0,0 +1,2 @@ +export * from './record-not-found.error-mapping'; +export * from './unique-violation.error-mapping'; diff --git a/nest-mongo-jwt/src/database/error-mappings/record-not-found.error-mapping.spec.ts b/nest-mongo-jwt/src/database/error-mappings/record-not-found.error-mapping.spec.ts new file mode 100644 index 00000000..bbf3b076 --- /dev/null +++ b/nest-mongo-jwt/src/database/error-mappings/record-not-found.error-mapping.spec.ts @@ -0,0 +1,38 @@ +import { RecordNotFoundError } from '@database/errors'; +import { MongoError } from 'mongodb'; +import { recordNotFoundErrorMapping } from './record-not-found.error-mapping'; + +describe('recordNotFound', () => { + it('should return an error mapping', () => { + expect(recordNotFoundErrorMapping('error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = recordNotFoundErrorMapping('error-message').isError; + + it('should return true when the error is a MongoDB error', () => { + const error = new MongoError('error'); + error.code = 1; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a MongoDB error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = recordNotFoundErrorMapping('error-message').newError; + + it('should return an instance of RecordNotFound with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(RecordNotFoundError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/nest-mongo-jwt/src/database/error-mappings/record-not-found.error-mapping.ts b/nest-mongo-jwt/src/database/error-mappings/record-not-found.error-mapping.ts new file mode 100644 index 00000000..f0233269 --- /dev/null +++ b/nest-mongo-jwt/src/database/error-mappings/record-not-found.error-mapping.ts @@ -0,0 +1,8 @@ +import { RecordNotFoundError } from '@database/errors'; +import { isDatabaseError } from '@database/utils'; +import { ErrorMapping } from '@utils/error'; + +export const recordNotFoundErrorMapping = (message: string): ErrorMapping => ({ + isError: isDatabaseError, + newError: () => new RecordNotFoundError(message), +}); diff --git a/nest-mongo-jwt/src/database/error-mappings/unique-violation.error-mapping.spec.ts b/nest-mongo-jwt/src/database/error-mappings/unique-violation.error-mapping.spec.ts new file mode 100644 index 00000000..563d910d --- /dev/null +++ b/nest-mongo-jwt/src/database/error-mappings/unique-violation.error-mapping.spec.ts @@ -0,0 +1,38 @@ +import { uniqueViolation } from './unique-violation.error-mapping'; +import { DuplicateRecordError } from '@database/errors'; +import { MongoError } from 'mongodb'; + +describe('uniqueViolation', () => { + it('should return an error mapping', () => { + expect(uniqueViolation('error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = uniqueViolation('error-message').isError; + + it('should return true when the error is a unique constraint violation error', () => { + const error = new MongoError('error'); + error.code = 11000; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a unique constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = uniqueViolation('error-message').newError; + + it('should return an instance of DuplicateRecord with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(DuplicateRecordError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/nest-mongo-jwt/src/database/error-mappings/unique-violation.error-mapping.ts b/nest-mongo-jwt/src/database/error-mappings/unique-violation.error-mapping.ts new file mode 100644 index 00000000..b190ef2e --- /dev/null +++ b/nest-mongo-jwt/src/database/error-mappings/unique-violation.error-mapping.ts @@ -0,0 +1,8 @@ +import { DuplicateRecordError } from '@database/errors'; +import { isUniqueViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const uniqueViolation = (message: string): ErrorMapping => ({ + isError: isUniqueViolation, + newError: () => new DuplicateRecordError(message), +}); diff --git a/assets/nest/example-app-pg/src/database/errors/duplicate-record.error.ts b/nest-mongo-jwt/src/database/errors/duplicate-record.error.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/errors/duplicate-record.error.ts rename to nest-mongo-jwt/src/database/errors/duplicate-record.error.ts diff --git a/assets/nest/example-app-pg/src/database/errors/index.ts b/nest-mongo-jwt/src/database/errors/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/errors/index.ts rename to nest-mongo-jwt/src/database/errors/index.ts diff --git a/assets/nest/example-app-pg/src/database/errors/record-not-found.error.ts b/nest-mongo-jwt/src/database/errors/record-not-found.error.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/errors/record-not-found.error.ts rename to nest-mongo-jwt/src/database/errors/record-not-found.error.ts diff --git a/nest-mongo-jwt/src/database/utils/error.ts b/nest-mongo-jwt/src/database/utils/error.ts new file mode 100644 index 00000000..80281f4e --- /dev/null +++ b/nest-mongo-jwt/src/database/utils/error.ts @@ -0,0 +1,7 @@ +import { MongoError } from 'mongodb'; + +export const isDatabaseError = (e: unknown): e is MongoError => + e instanceof MongoError && e.code !== undefined; + +export const isUniqueViolation = (e: unknown) => + isDatabaseError(e) && e.code === 11000; diff --git a/assets/nest/example-app-pg/src/database/utils/index.ts b/nest-mongo-jwt/src/database/utils/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/utils/index.ts rename to nest-mongo-jwt/src/database/utils/index.ts diff --git a/nest-mongo-jwt/src/main.ts b/nest-mongo-jwt/src/main.ts new file mode 100644 index 00000000..d27d0b48 --- /dev/null +++ b/nest-mongo-jwt/src/main.ts @@ -0,0 +1,65 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import helmet from '@fastify/helmet'; +import compression from '@fastify/compress'; +import { HttpAdapterHost, NestFactory } from '@nestjs/core'; +import { ConfigType } from '@nestjs/config'; +import { AppModule } from './app.module'; +import { ValidationPipe } from '@nestjs/common'; +import { RequestLoggingInterceptor } from '@utils/interceptors/request-logging.interceptor'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { ErrorLoggingFilter } from '@utils/error-logging.filter'; +import { nodeConfig } from '@utils/environment'; + +async function bootstrap() { + const app = await NestFactory.create( + AppModule, + new FastifyAdapter(), + ); + + // enables CORS + app.enableCors(); + + // add security HTTP headers + app.register(helmet); + + // compresses response bodies + app.register(compression); + + // enable validation globally + app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); + + // map application level errors to http errors + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + // setup graceful shutdown + app.enableShutdownHooks(); + + const configService = app.get>(nodeConfig.KEY); + const port = configService.PORT; + + if (configService.REQUEST_LOGGING) { + app.useGlobalInterceptors(new RequestLoggingInterceptor()); + } + + if (configService.SWAGGER) { + const config = new DocumentBuilder().setTitle('To-Do Example API').build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('/swagger', app, document); + } + + if (configService.ERROR_LOGGING) { + const httpAdapterHost = app.get(HttpAdapterHost); + app.useGlobalFilters(new ErrorLoggingFilter(httpAdapterHost)); + } + + // start server + await app.listen(port, '0.0.0.0', () => { + console.log(`App is running on http://localhost:${port}`); + }); +} + +bootstrap(); diff --git a/assets/nest/example-app-pg/src/utils/api/index.ts b/nest-mongo-jwt/src/utils/api/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/api/index.ts rename to nest-mongo-jwt/src/utils/api/index.ts diff --git a/nest-mongo-jwt/src/utils/api/response.ts b/nest-mongo-jwt/src/utils/api/response.ts new file mode 100644 index 00000000..0759d778 --- /dev/null +++ b/nest-mongo-jwt/src/utils/api/response.ts @@ -0,0 +1,16 @@ +import { Paginated, Pagination, extractPagination } from '@utils/query'; + +export const paginatedResponse = ( + items: T[], + total: number, + pagination?: Pagination +): Paginated => { + const { pageNumber, pageSize } = extractPagination(pagination); + + return { + items, + total: total, + currentPage: pageNumber, + totalPages: Math.ceil(total / pageSize), + }; +}; diff --git a/nest-mongo-jwt/src/utils/class-transformers/index.ts b/nest-mongo-jwt/src/utils/class-transformers/index.ts new file mode 100644 index 00000000..94fd1349 --- /dev/null +++ b/nest-mongo-jwt/src/utils/class-transformers/index.ts @@ -0,0 +1,3 @@ +export * from './lower-case'; +export * from './object-id'; +export * from './trim'; diff --git a/nest-mongo-jwt/src/utils/class-transformers/lower-case.ts b/nest-mongo-jwt/src/utils/class-transformers/lower-case.ts new file mode 100644 index 00000000..dcc3b625 --- /dev/null +++ b/nest-mongo-jwt/src/utils/class-transformers/lower-case.ts @@ -0,0 +1,16 @@ +import { Transform } from 'class-transformer'; + +const toLower = (v: string) => v.toLowerCase(); + +export const LowerCase = () => + Transform( + (params) => + Array.isArray(params.value) + ? params.value.map(toLower) + : typeof params.value === 'string' + ? toLower(params.value) + : params.value, + { + toClassOnly: true, + } + ); diff --git a/nest-mongo-jwt/src/utils/class-transformers/object-id.ts b/nest-mongo-jwt/src/utils/class-transformers/object-id.ts new file mode 100644 index 00000000..0fa9e1ed --- /dev/null +++ b/nest-mongo-jwt/src/utils/class-transformers/object-id.ts @@ -0,0 +1,16 @@ +import { BadRequestDto } from '@utils/dtos'; +import { Transform } from 'class-transformer'; +import { ObjectId } from 'mongodb'; + +const toObjectId = (v: string) => { + try { + return new ObjectId(v); + } catch (err) { + throw new BadRequestDto(); + } +}; + +export const ToObjectId = () => + Transform((params) => toObjectId(params.value), { + toClassOnly: true, + }); diff --git a/assets/nest/example-app-pg/src/utils/class-transformers/trim.ts b/nest-mongo-jwt/src/utils/class-transformers/trim.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/class-transformers/trim.ts rename to nest-mongo-jwt/src/utils/class-transformers/trim.ts diff --git a/assets/nest/example-app-pg/src/utils/decorators/index.ts b/nest-mongo-jwt/src/utils/decorators/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/decorators/index.ts rename to nest-mongo-jwt/src/utils/decorators/index.ts diff --git a/assets/nest/example-app-pg/src/utils/decorators/public.decorator.spec.ts b/nest-mongo-jwt/src/utils/decorators/public.decorator.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/decorators/public.decorator.spec.ts rename to nest-mongo-jwt/src/utils/decorators/public.decorator.spec.ts diff --git a/assets/nest/example-app-pg/src/utils/decorators/public.decorator.ts b/nest-mongo-jwt/src/utils/decorators/public.decorator.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/decorators/public.decorator.ts rename to nest-mongo-jwt/src/utils/decorators/public.decorator.ts diff --git a/assets/nest/example-app-pg/src/utils/dtos/errors.dto.ts b/nest-mongo-jwt/src/utils/dtos/errors.dto.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/dtos/errors.dto.ts rename to nest-mongo-jwt/src/utils/dtos/errors.dto.ts diff --git a/assets/nest/example-app-pg/src/utils/dtos/index.ts b/nest-mongo-jwt/src/utils/dtos/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/dtos/index.ts rename to nest-mongo-jwt/src/utils/dtos/index.ts diff --git a/assets/nest/example-app-pg/src/utils/dtos/pagination.dto.ts b/nest-mongo-jwt/src/utils/dtos/pagination.dto.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/dtos/pagination.dto.ts rename to nest-mongo-jwt/src/utils/dtos/pagination.dto.ts diff --git a/nest-mongo-jwt/src/utils/entities/generic-entity.ts b/nest-mongo-jwt/src/utils/entities/generic-entity.ts new file mode 100644 index 00000000..1d2e949c --- /dev/null +++ b/nest-mongo-jwt/src/utils/entities/generic-entity.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ToObjectId } from '@utils/class-transformers'; +import { ObjectId } from 'mongodb'; + +export class GenericEntity { + @ApiProperty() + @ToObjectId() + _id: ObjectId; + + @ApiProperty() + createdAt: number; + + @ApiProperty() + updatedAt: number; +} diff --git a/assets/nest/example-app-pg/src/utils/entities/index.ts b/nest-mongo-jwt/src/utils/entities/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/entities/index.ts rename to nest-mongo-jwt/src/utils/entities/index.ts diff --git a/assets/nest/example-app-pg/src/utils/enums/errors.enum.ts b/nest-mongo-jwt/src/utils/enums/errors.enum.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/enums/errors.enum.ts rename to nest-mongo-jwt/src/utils/enums/errors.enum.ts diff --git a/assets/nest/example-app-pg/src/utils/enums/index.ts b/nest-mongo-jwt/src/utils/enums/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/enums/index.ts rename to nest-mongo-jwt/src/utils/enums/index.ts diff --git a/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.spec.ts b/nest-mongo-jwt/src/utils/environment.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.spec.ts rename to nest-mongo-jwt/src/utils/environment.spec.ts diff --git a/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.ts b/nest-mongo-jwt/src/utils/environment.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.ts rename to nest-mongo-jwt/src/utils/environment.ts diff --git a/assets/nest/example-app-pg/src/utils/error-logging.filter.spec.ts b/nest-mongo-jwt/src/utils/error-logging.filter.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/error-logging.filter.spec.ts rename to nest-mongo-jwt/src/utils/error-logging.filter.spec.ts diff --git a/assets/nest/example-app-pg/src/utils/error-logging.filter.ts b/nest-mongo-jwt/src/utils/error-logging.filter.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/error-logging.filter.ts rename to nest-mongo-jwt/src/utils/error-logging.filter.ts diff --git a/nest-mongo-jwt/src/utils/error.spec.ts b/nest-mongo-jwt/src/utils/error.spec.ts new file mode 100644 index 00000000..5550dd9a --- /dev/null +++ b/nest-mongo-jwt/src/utils/error.spec.ts @@ -0,0 +1,59 @@ +import { ErrorMapping, mapError, rethrowError } from './error'; + +describe('mapError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const newError = new TypeError('type-error'); + + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return newError; + }, + }; + + it('should return the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(mapError(mapping)(error)).toBe(error); + }); + + it('should return the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(mapError(mapping)(error)).toBe(newError); + }); + }); +}); + +describe('rethrowError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return new TypeError('type-error'); + }, + }; + + it('should rethrow the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(() => rethrowError(mapping)(error)).toThrow(error); + }); + + it('should rethrow the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(() => rethrowError(mapping)(error)).toThrow( + new TypeError('type-error'), + ); + }); + }); +}); diff --git a/assets/nest/example-app-pg/src/utils/error.ts b/nest-mongo-jwt/src/utils/error.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/error.ts rename to nest-mongo-jwt/src/utils/error.ts diff --git a/assets/nest/example-app-pg/src/utils/interceptors/index.ts b/nest-mongo-jwt/src/utils/interceptors/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/interceptors/index.ts rename to nest-mongo-jwt/src/utils/interceptors/index.ts diff --git a/assets/nest/example-app-pg/src/utils/interceptors/request-logging.interceptor.ts b/nest-mongo-jwt/src/utils/interceptors/request-logging.interceptor.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/interceptors/request-logging.interceptor.ts rename to nest-mongo-jwt/src/utils/interceptors/request-logging.interceptor.ts diff --git a/nest-mongo-jwt/src/utils/interceptors/service-to-http-error.interceptor.ts b/nest-mongo-jwt/src/utils/interceptors/service-to-http-error.interceptor.ts new file mode 100644 index 00000000..3e6497c1 --- /dev/null +++ b/nest-mongo-jwt/src/utils/interceptors/service-to-http-error.interceptor.ts @@ -0,0 +1,31 @@ +import { + CallHandler, + ConflictException, + ExecutionContext, + Injectable, + NestInterceptor, + NotFoundException, +} from '@nestjs/common'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { DuplicateRecordError, RecordNotFoundError } from '@database/errors'; + +const map = { + [RecordNotFoundError.name]: NotFoundException, + [DuplicateRecordError.name]: ConflictException, +}; + +@Injectable() +export class ServiceToHttpErrorsInterceptor implements NestInterceptor { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + catchError((err: Error) => + throwError(() => { + const klass = map[err.name]; + return klass ? new klass(err.message) : err; + }) + ) + ); + } +} diff --git a/nest-mongo-jwt/src/utils/interfaces/index.ts b/nest-mongo-jwt/src/utils/interfaces/index.ts new file mode 100644 index 00000000..1e2f37af --- /dev/null +++ b/nest-mongo-jwt/src/utils/interfaces/index.ts @@ -0,0 +1,7 @@ +export type IsNullable = null extends T ? K : never; + +export type NullableKeys = { [K in keyof T]: IsNullable }[keyof T]; + +/** `Partial` just for the nullable keys */ +export type NullableKeysPartial = Omit> & + Partial>>; diff --git a/nest-mongo-jwt/src/utils/pipes/index.ts b/nest-mongo-jwt/src/utils/pipes/index.ts new file mode 100644 index 00000000..26633dc2 --- /dev/null +++ b/nest-mongo-jwt/src/utils/pipes/index.ts @@ -0,0 +1 @@ +export * from './to-object-id'; diff --git a/nest-mongo-jwt/src/utils/pipes/to-object-id.spec.ts b/nest-mongo-jwt/src/utils/pipes/to-object-id.spec.ts new file mode 100644 index 00000000..36157487 --- /dev/null +++ b/nest-mongo-jwt/src/utils/pipes/to-object-id.spec.ts @@ -0,0 +1,22 @@ +import { BadRequestException } from '@nestjs/common'; +import { ObjectId } from 'mongodb'; +import { ToObjectIdPipe } from './to-object-id'; + +describe('To ObjectId Pipe', () => { + let pipe: ToObjectIdPipe; + beforeEach(() => { + pipe = new ToObjectIdPipe(); + }); + + it('converts the value to ObjectId when the input is of valid type', () => { + const strObjectId = new ObjectId(); + + expect(pipe.transform(strObjectId)).toEqual(strObjectId); + }); + + it('throws an error when the input is of invalid type', () => { + expect(() => pipe.transform('test')).toThrow( + new BadRequestException('Invalid Id format') + ); + }); +}); diff --git a/nest-mongo-jwt/src/utils/pipes/to-object-id.ts b/nest-mongo-jwt/src/utils/pipes/to-object-id.ts new file mode 100644 index 00000000..9de7aa32 --- /dev/null +++ b/nest-mongo-jwt/src/utils/pipes/to-object-id.ts @@ -0,0 +1,13 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { ObjectId } from 'mongodb'; + +@Injectable() +export class ToObjectIdPipe implements PipeTransform { + public transform(value: any): ObjectId { + try { + return new ObjectId(value); + } catch (err) { + throw new BadRequestException('Invalid Id format'); + } + } +} diff --git a/nest-mongo-jwt/src/utils/query/error.spec.ts b/nest-mongo-jwt/src/utils/query/error.spec.ts new file mode 100644 index 00000000..08fd1ae9 --- /dev/null +++ b/nest-mongo-jwt/src/utils/query/error.spec.ts @@ -0,0 +1,47 @@ +import { RecordNotFoundError } from '@database/errors'; +import { definedOrNotFound, updatedOrNotFound } from './error'; + +describe('definedOrNotFound', () => { + it('should return a function', () => { + expect(typeof definedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the value it is called with when the value is not undefined', () => { + expect(definedOrNotFound('message')(undefined)).toBe(undefined); + expect(definedOrNotFound('message')(false)).toBe(false); + expect(definedOrNotFound('message')(0)).toBe(0); + expect(definedOrNotFound('message')('')).toBe(''); + const emptyArray: unknown[] = []; + expect(definedOrNotFound('message')(emptyArray)).toBe(emptyArray); + const emptyObject = {}; + expect(definedOrNotFound('message')(emptyObject)).toEqual(emptyObject); + }); + + it('should throw RecordNotFound when the value it is called with is undefined', () => { + expect(() => definedOrNotFound('message')(null)).toThrow( + new RecordNotFoundError('message'), + ); + }); + }); +}); + +describe('updatedOrNotFound', () => { + it('should return a function', () => { + expect(typeof updatedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the number it is called with when the number is not zero', () => { + expect(updatedOrNotFound('message')(-1)).toBe(-1); + expect(updatedOrNotFound('message')(1)).toBe(1); + expect(updatedOrNotFound('message')(NaN)).toEqual(NaN); + }); + + it('should throw RecordNotFound when the value it is called with is zero', () => { + expect(() => updatedOrNotFound('message')(0)).toThrow( + new RecordNotFoundError('message'), + ); + }); + }); +}); diff --git a/nest-mongo-jwt/src/utils/query/error.ts b/nest-mongo-jwt/src/utils/query/error.ts new file mode 100644 index 00000000..56c3448e --- /dev/null +++ b/nest-mongo-jwt/src/utils/query/error.ts @@ -0,0 +1,25 @@ +import { RecordNotFoundError } from '@database/errors'; + +const definedOrThrow = (errorFactory: () => Error) => { + return (result: T | null): T => { + if (result === null) { + throw errorFactory(); + } + return result; + }; +}; + +const updatedOrThrow = (errorFactory: () => Error) => { + return (result: number): number => { + if (result === 0) { + throw errorFactory(); + } + return result; + }; +}; + +export const definedOrNotFound = (message: string) => + definedOrThrow(() => new RecordNotFoundError(message)); + +export const updatedOrNotFound = (message: string) => + updatedOrThrow(() => new RecordNotFoundError(message)); diff --git a/nest-mongo-jwt/src/utils/query/index.ts b/nest-mongo-jwt/src/utils/query/index.ts new file mode 100644 index 00000000..33fe6e52 --- /dev/null +++ b/nest-mongo-jwt/src/utils/query/index.ts @@ -0,0 +1,4 @@ +export * from './error'; +// export * from './filtration'; +export * from './pagination'; +export * from './sorting'; diff --git a/assets/nest/example-app-pg/src/utils/query/pagination.spec.ts b/nest-mongo-jwt/src/utils/query/pagination.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/pagination.spec.ts rename to nest-mongo-jwt/src/utils/query/pagination.spec.ts diff --git a/assets/nest/example-app-pg/src/utils/query/pagination.ts b/nest-mongo-jwt/src/utils/query/pagination.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/pagination.ts rename to nest-mongo-jwt/src/utils/query/pagination.ts diff --git a/nest-mongo-jwt/src/utils/query/sorting.ts b/nest-mongo-jwt/src/utils/query/sorting.ts new file mode 100644 index 00000000..e9004397 --- /dev/null +++ b/nest-mongo-jwt/src/utils/query/sorting.ts @@ -0,0 +1,9 @@ +export enum SortOrder { + Asc = 'asc', + Desc = 'desc', +} + +export interface Sort { + column: SortColumn; + order?: SortOrder; +} diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/login.e2e-spec.ts b/nest-mongo-jwt/test/auth/login.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/login.e2e-spec.ts rename to nest-mongo-jwt/test/auth/login.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/register.e2e-spec.ts b/nest-mongo-jwt/test/auth/register.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/register.e2e-spec.ts rename to nest-mongo-jwt/test/auth/register.e2e-spec.ts diff --git a/assets/nest/example-app-pg/test/healthchecks.e2e-spec.ts b/nest-mongo-jwt/test/healthchecks.e2e-spec.ts similarity index 100% rename from assets/nest/example-app-pg/test/healthchecks.e2e-spec.ts rename to nest-mongo-jwt/test/healthchecks.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/jest-e2e.config.js b/nest-mongo-jwt/test/jest-e2e.config.js similarity index 81% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/jest-e2e.config.js rename to nest-mongo-jwt/test/jest-e2e.config.js index e4dadd49..21834cad 100644 --- a/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/jest-e2e.config.js +++ b/nest-mongo-jwt/test/jest-e2e.config.js @@ -2,21 +2,15 @@ * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - moduleFileExtensions: [ - 'js', - 'json', - 'ts' - ], + moduleFileExtensions: ['js', 'json', 'ts'], rootDir: '..', testEnvironment: 'node', testRegex: '.e2e-spec.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest' + '^.+\\.(t|j)s$': 'ts-jest', }, coverageDirectory: 'coverage-e2e', - collectCoverageFrom: [ - '/src/api/**/*.(t|j)s' - ], + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], coveragePathIgnorePatterns: [ '/src/main.ts$', '/.*spec.ts$', @@ -26,16 +20,16 @@ module.exports = { '/src/.*entity.ts$', '/src/.*repository.ts$', '/src/.*guard.ts$', - '/src/api/auth/.*.ts$', ], coverageThreshold: { global: { branches: 85, functions: 85, lines: 85, - statements: 85 - } + statements: 85, + }, }, + setupFiles: ['/jest.setup.ts'], // path aliases from tsconfig.json moduleNameMapper: { '^@api/(.*)$': '/src/api/$1', @@ -46,4 +40,4 @@ module.exports = { '^@utils/(.*)$': '/src/utils/$1', '^@test/(.*)$': '/test/$1', }, -} +}; diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/create.e2e-spec.ts b/nest-mongo-jwt/test/todos/create.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/create.e2e-spec.ts rename to nest-mongo-jwt/test/todos/create.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-all.e2e-spec.ts b/nest-mongo-jwt/test/todos/find-all.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-all.e2e-spec.ts rename to nest-mongo-jwt/test/todos/find-all.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-one.e2e-spec.ts b/nest-mongo-jwt/test/todos/find-one.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-one.e2e-spec.ts rename to nest-mongo-jwt/test/todos/find-one.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/remove.e2e-spec.ts b/nest-mongo-jwt/test/todos/remove.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/remove.e2e-spec.ts rename to nest-mongo-jwt/test/todos/remove.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/update.e2e-spec.ts b/nest-mongo-jwt/test/todos/update.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/update.e2e-spec.ts rename to nest-mongo-jwt/test/todos/update.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/get-todo-payload.ts b/nest-mongo-jwt/test/todos/utils/get-todo-payload.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/get-todo-payload.ts rename to nest-mongo-jwt/test/todos/utils/get-todo-payload.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/index.ts b/nest-mongo-jwt/test/todos/utils/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/index.ts rename to nest-mongo-jwt/test/todos/utils/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/sortby-field-todos.ts b/nest-mongo-jwt/test/todos/utils/sortby-field-todos.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/sortby-field-todos.ts rename to nest-mongo-jwt/test/todos/utils/sortby-field-todos.ts diff --git a/nest-mongo-jwt/test/utils/db-setup.ts b/nest-mongo-jwt/test/utils/db-setup.ts new file mode 100644 index 00000000..30ea1183 --- /dev/null +++ b/nest-mongo-jwt/test/utils/db-setup.ts @@ -0,0 +1,56 @@ +import { Collections } from '@database/constants'; +import { Db, ObjectId } from 'mongodb'; + +export class MongoDBTestSetup { + public readonly userId: ObjectId; + + constructor(private readonly mongodb: Db) { + this.userId = new ObjectId(); + this.mongodb = mongodb; + } + + async seedData() { + await this.mongodb.createCollection(Collections.Users); + await this.mongodb.createCollection(Collections.Todos); + await this.mongodb.createIndex(Collections.Todos, 'user_id'); + await seedDb(this.mongodb, this.userId); + } + + async removeSeededData() { + await this.mongodb.dropCollection(Collections.Todos); + await this.mongodb.dropCollection(Collections.Users); + } +} + +async function seedDb(mongodb: Db, userId: ObjectId) { + await mongodb.collection('users').deleteMany(); + await mongodb.collection('users').insertOne( + // The original password for this hash is 'pass@ord' + { + email: 'hello@email.com', + password: '$2b$10$Mxur7NOiTlm22yuldEMZgOCbIV7bxDCcUbBLFbzrJ1MrnIczZB.92', // pragma: allowlist secret + userId, + }, + ); + await mongodb.collection('todos').deleteMany(); + await mongodb.collection('todos').insertMany([ + { + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId, + }, + { + name: 'Laundry 2', + note: 'Buy detergent 2', + completed: false, + userId, + }, + { + name: 'Laundry 3', + note: 'Buy detergent 3', + completed: true, + userId, + }, + ]); +} diff --git a/nest-mongo-jwt/test/utils/expect-error.ts b/nest-mongo-jwt/test/utils/expect-error.ts new file mode 100644 index 00000000..6837da87 --- /dev/null +++ b/nest-mongo-jwt/test/utils/expect-error.ts @@ -0,0 +1,11 @@ +import { HttpException } from '@nestjs/common'; + +export const expectError = async ( + ex: T, + jsonResponse: Response['json'] +) => { + const response = await jsonResponse(); + + expect(ex.message).toEqual(response.error || response.message); + expect(ex.getStatus()).toEqual(response.statusCode); +}; diff --git a/nest-mongo-jwt/test/utils/index.ts b/nest-mongo-jwt/test/utils/index.ts new file mode 100644 index 00000000..cad2ae0f --- /dev/null +++ b/nest-mongo-jwt/test/utils/index.ts @@ -0,0 +1,2 @@ +export * from './expect-error'; +export * from './db-setup'; diff --git a/assets/nest/example-app-pg/tsconfig.build.json b/nest-mongo-jwt/tsconfig.build.json similarity index 100% rename from assets/nest/example-app-pg/tsconfig.build.json rename to nest-mongo-jwt/tsconfig.build.json diff --git a/nest-mongo-jwt/tsconfig.json b/nest-mongo-jwt/tsconfig.json new file mode 100644 index 00000000..b31a3ff6 --- /dev/null +++ b/nest-mongo-jwt/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "paths": { + "@api/*": ["src/api/*"], + "@database": ["src/database"], + "@database/*": ["src/database/*"], + "@extensions/*": ["src/extensions/*"], + "@middleware": ["src/middleware"], + "@utils/*": ["src/utils/*"], + "@test/*": ["test/*"] + } + }, + "ts-node": { + "require": ["tsconfig-paths/register", "dotenv/config"] + } +} diff --git a/nest-pg-auth0/.commitlintrc.js b/nest-pg-auth0/.commitlintrc.js new file mode 100755 index 00000000..b0f2e40c --- /dev/null +++ b/nest-pg-auth0/.commitlintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {}, + parserPreset: { + parserOpts: { + referenceActions: null, + issuePrefixes: ['AB#'], + }, + }, +}; diff --git a/nest-pg-auth0/.czrc b/nest-pg-auth0/.czrc new file mode 100644 index 00000000..64b8fecd --- /dev/null +++ b/nest-pg-auth0/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/nest-pg-auth0/.devcontainer/devcontainer.json b/nest-pg-auth0/.devcontainer/devcontainer.json new file mode 100644 index 00000000..3ff38d20 --- /dev/null +++ b/nest-pg-auth0/.devcontainer/devcontainer.json @@ -0,0 +1,9 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Nest API DevContainer", + "dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"], + "service": "api", + "workspaceFolder": "/usr/src/app", + "remoteUser": "root" +} diff --git a/nest-pg-auth0/.devcontainer/docker-compose.yml b/nest-pg-auth0/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..b01c619a --- /dev/null +++ b/nest-pg-auth0/.devcontainer/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + api: + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" diff --git a/nest-pg-auth0/.dockerignore b/nest-pg-auth0/.dockerignore new file mode 100644 index 00000000..24da266d --- /dev/null +++ b/nest-pg-auth0/.dockerignore @@ -0,0 +1,15 @@ +.env* +.eslint* +.git +.husky +.nvmrc +.prettier* +.vscode +coverage +coverage-e2e +dist +docker-compose* +Dockerfile +node_modules +README.md +test diff --git a/nest-pg-auth0/.editorconfig b/nest-pg-auth0/.editorconfig new file mode 100644 index 00000000..5e1950da --- /dev/null +++ b/nest-pg-auth0/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/nest-pg-auth0/.env.example b/nest-pg-auth0/.env.example new file mode 100644 index 00000000..112d2ef3 --- /dev/null +++ b/nest-pg-auth0/.env.example @@ -0,0 +1,29 @@ +# Node +NODE_ENV=development + +# HTTP +PORT=3000 + +# Logging +ERROR_LOGGING=true +REQUEST_LOGGING=true + +# Swagger +SWAGGER=true + +# Knex +DEBUG=knex:query + +# PostgreSQL +PGHOST=localhost +PGPORT=5432 +PGUSER=user +PGPASSWORD=password +PGDATABASE=default + +# AUTH0 +AUTH0_ISSUER_URL=https://mock.auth0.com/api/v2/ +AUTH0_CLIENT_ID=CLIENT_ID +AUTH0_AUDIENCE=AUDIENCE +AUTH0_CLIENT_SECRET=CLIENT_SECRET + diff --git a/nest-pg-auth0/.eslintignore b/nest-pg-auth0/.eslintignore new file mode 100755 index 00000000..0589ba50 --- /dev/null +++ b/nest-pg-auth0/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.vscode +coverage +dist/ +*.config.js +*rc.js +src/extensions/**/*.js +licenses-allowed.js diff --git a/nest-pg-auth0/.eslintrc.js b/nest-pg-auth0/.eslintrc.js new file mode 100644 index 00000000..259de13c --- /dev/null +++ b/nest-pg-auth0/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + 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/nest-pg-auth0/.github/workflows/audit.yaml b/nest-pg-auth0/.github/workflows/audit.yaml new file mode 100644 index 00000000..d631240e --- /dev/null +++ b/nest-pg-auth0/.github/workflows/audit.yaml @@ -0,0 +1,16 @@ +name: audit + +on: + pull_request: + branches: + - main + +jobs: + audit: + name: Audit packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run audit + run: npm audit --omit=dev diff --git a/nest-pg-auth0/.github/workflows/coverage-e2e.yaml b/nest-pg-auth0/.github/workflows/coverage-e2e.yaml new file mode 100644 index 00000000..599f71aa --- /dev/null +++ b/nest-pg-auth0/.github/workflows/coverage-e2e.yaml @@ -0,0 +1,42 @@ +name: coverage-e2e + +on: + pull_request: + branches: + - main + +env: + NODE_ENV: test + DEBUG: '' + PORT: 3000 + PGHOST: localhost + PGPORT: 5432 + PGUSER: user + PGPASSWORD: password + PGDATABASE: tests + JWT_SECRET: super-secret + JWT_EXPIRATION: 7200 + +jobs: + coverage-e2e: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Create database + uses: isbang/compose-action@v1.4.1 + - name: Run coverage + uses: ArtiomTr/jest-coverage-report-action@v2 + with: + test-script: npm run test:e2e diff --git a/nest-pg-auth0/.github/workflows/coverage.yaml b/nest-pg-auth0/.github/workflows/coverage.yaml new file mode 100644 index 00000000..8de35d91 --- /dev/null +++ b/nest-pg-auth0/.github/workflows/coverage.yaml @@ -0,0 +1,26 @@ +name: coverage + +on: + pull_request: + branches: + - main + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Get test coverage + uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/nest-pg-auth0/.gitignore b/nest-pg-auth0/.gitignore new file mode 100644 index 00000000..008b70cd --- /dev/null +++ b/nest-pg-auth0/.gitignore @@ -0,0 +1,156 @@ +# This is a slightly modified version of https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# OS +.DS_Store + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage/ +coverage-e2e/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional prettier cache +.prettiercache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +# .env +.env* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Docker compose +docker-compose.override.yml + +# 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 diff --git a/nest-pg-auth0/.husky/.gitignore b/nest-pg-auth0/.husky/.gitignore new file mode 100644 index 00000000..c9cdc63b --- /dev/null +++ b/nest-pg-auth0/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/nest-pg-auth0/.husky/commit-msg b/nest-pg-auth0/.husky/commit-msg new file mode 100755 index 00000000..9e8dfba1 --- /dev/null +++ b/nest-pg-auth0/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit $1 diff --git a/nest-pg-auth0/.husky/pre-commit b/nest-pg-auth0/.husky/pre-commit new file mode 100755 index 00000000..2151bed3 --- /dev/null +++ b/nest-pg-auth0/.husky/pre-commit @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- tsc --noEmit +npx --no -- lint-staged +npx --no -- ls-lint +npm run lint:dockerfile +npm run license:check +npm run test:cov +npm run test:e2e:cov +scripts/detect-secrets.sh + +# Enable branch naming validation by uncommenting the lines below and adjust valid_branch_regex to your preference +# # Enforce branch naming conventions +# LC_ALL=C + +# # Git branch will return an empty line before the initial commit +# git_branch=$(git branch) + +# if [ -z "$git_branch" ]; then +# echo "Initial commit, no branch name linting" +# exit 0 +# fi + +# # INSERT A REGEX PATTERN ACCORDING TO YOUR PROJECT BRANCH NAMING CONVENTION. +# branch_regex="^(chore|feat|fix|docs|style|refactor|test|revert|ci)\/[a-z0-9._-]+$" + +# # Get the branch name +# branch="$(git rev-parse --abbrev-ref HEAD)" + +# # UPDATE THE EXAMPLES TO MATCH YOUR PROJECT BRANCH NAMING CONVENTION. +# error_message="Branch name validation failed. Branch name must adhere to: /$branch_regex/. +# You should rename your branch to a valid name and try again. Here are some examples of valid branch names: + +# feat/my-feature +# test/my-feature +# fix/that-bug +# chore/my-task +# docs/update-readme +# refactor/do-thing-differently +# ci/update-build-script +# " + + +# # Use grep to do the matching, as `if [[ ]]` is not available in `sh` +# match=$(echo $branch | grep -E "$branch_regex") || echo "" + +# # Check if the match is a non-empty string +# if [ -z "$match" ]; then +# echo "$error_message" +# exit 1 +# fi diff --git a/nest-pg-auth0/.husky/pre-push b/nest-pg-auth0/.husky/pre-push new file mode 100755 index 00000000..b9c1cb2f --- /dev/null +++ b/nest-pg-auth0/.husky/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" diff --git a/nest-pg-auth0/.lintstagedrc b/nest-pg-auth0/.lintstagedrc new file mode 100644 index 00000000..22e14328 --- /dev/null +++ b/nest-pg-auth0/.lintstagedrc @@ -0,0 +1,12 @@ +{ + "*.{js,cjs,mjs,ts}": [ + "prettier --write", + "eslint --fix" + ], + "*.md": [ + "prettier --write", + "markdownlint --fix" + ], + "*.sh": "shellcheck -x", + "package.json": "sort-package-json" +} \ No newline at end of file diff --git a/nest-pg-auth0/.ls-lint.yml b/nest-pg-auth0/.ls-lint.yml new file mode 100644 index 00000000..024d598f --- /dev/null +++ b/nest-pg-auth0/.ls-lint.yml @@ -0,0 +1,30 @@ +ls: + # directories + .dir: kebab-case | regex:__tests__ | regex:__mocks__ | regex:__data__ + + # shell scripts + .sh: kebab-case + + # source files + # this is essentially kebab-case.with-custom.extentions.js + .js: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + .ts: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + + # migrations + migrations: + .ts: snake_case + +# ignored directories and files +ignore: + - node_modules + - .git + - .github + - .vscode + - .husky + - dist + - .commitlintrc.js + - .eslintrc.js + - .prettierrc.js + - .openapi + - src/@types + - .devcontainer diff --git a/nest-pg-auth0/.nvmrc b/nest-pg-auth0/.nvmrc new file mode 100644 index 00000000..d3e33db8 --- /dev/null +++ b/nest-pg-auth0/.nvmrc @@ -0,0 +1 @@ +v18.16 diff --git a/nest-pg-auth0/.prettierignore b/nest-pg-auth0/.prettierignore new file mode 100644 index 00000000..b9a7a81d --- /dev/null +++ b/nest-pg-auth0/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +coverage +coverage-e2e +*.config.js +*rc.js diff --git a/nest-pg-auth0/.prettierrc b/nest-pg-auth0/.prettierrc new file mode 100644 index 00000000..dcb72794 --- /dev/null +++ b/nest-pg-auth0/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/nest-pg-auth0/.secrets.baseline b/nest-pg-auth0/.secrets.baseline new file mode 100644 index 00000000..d4cf5c2e --- /dev/null +++ b/nest-pg-auth0/.secrets.baseline @@ -0,0 +1,116 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-20T21:30:08Z" +} diff --git a/nest-pg-auth0/Dockerfile b/nest-pg-auth0/Dockerfile new file mode 100644 index 00000000..759bf918 --- /dev/null +++ b/nest-pg-auth0/Dockerfile @@ -0,0 +1,89 @@ +# +# 🧑‍💻 Development +# +FROM node:18-alpine AS dev + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Set the working directory +WORKDIR /usr/src/app + +# Set to dev environment +ENV NODE_ENV development + +# Copy the source code +COPY --chown=node:node . . + +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # rebuild bcrypt + && npm rebuild bcrypt + +# +# 🏡 Production Build +# +FROM node:18-alpine as build + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# In order to run `npm run build` we need access to the Nest CLI. +# Nest CLI is a dev dependency. +COPY --chown=node:node --from=dev /usr/src/app/node_modules ./node_modules +# Copy source code +COPY --chown=node:node . . + +# Generate the production build. The build script runs "nest build" to compile the application. +RUN npm run build + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # skip dev dependencies + --omit=dev \ + # rebuild bcrypt + && npm rebuild bcrypt \ + # attempt to reduce image size + && npm cache clean --force + +# Set Docker as a non-root user +USER node + +# +# 🚀 Production Server +# +FROM node:18-alpine as prod + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# Copy only the necessary files +COPY --chown=node:node --from=build /usr/src/app/dist dist +COPY --chown=node:node --from=build /usr/src/app/node_modules node_modules + +# Set Docker as non-root user +USER node + +# Run +CMD [ "node", "./dist/src/main.js" ] \ No newline at end of file diff --git a/nest-pg-auth0/README.md b/nest-pg-auth0/README.md new file mode 100644 index 00000000..016902dd --- /dev/null +++ b/nest-pg-auth0/README.md @@ -0,0 +1,260 @@ +# nest-pg-auth0 + +## Setup + +### Prerequisites + +#### Node and NPM via NVM + +**Install NVM:** + +Follow the instructions [here](https://github.com/nvm-sh/nvm#installing-and-updating). + +**Install the version of node specified in the `.nvmrc` file and switch to it:** + +```bash +nvm install +nvm use +``` + +**(Optional) Set the node version as default:** + +```bash +nvm alias default $(cat .nvmrc) +``` + +**(Optional) Automatically switch Node version when changing directories:** + +Follow the instructions for your shell [here](https://github.com/nvm-sh/nvm#deeper-shell-integration). + +#### Docker + +Follow the instructions [here](https://docs.docker.com/engine/install/). + +**Post installation steps for Linux users:** + +Follow the instructions [here](https://docs.docker.com/engine/install/linux-postinstall/). + +**Docker Compose:** + +In case `docker compose` isn't setup with the docker installation follow the +instructions [here](https://docs.docker.com/compose/install/). + +### Clone the repo and cd into the folder + +```bash +git clone git@github.com:.git +cd +``` + +### Install Node modules + +```bash +npm install +``` + +### Setup environment variables + +Copy the `.env.example` file to `.env`. Update environment variables as needed. + +```bash +cp .env.example .env +``` + +### Setup environment variables for e2e tests + +Copy the `.env` file to `.env.test`. Update environment variables as needed. + +```bash +cp .env .env.test +``` + +Update `NODE_ENV` to `test`. + +Make sure to provide a different database name from what you have in `.env` +as e2e tests wipe the database clean before they run. + +You may want to disable query logging by removing `DEBUG=knex:query`. + +### Setup docker-compose overrides + +Create a `docker-compose.override.yml` file using the example: + +```bash +cp docker-compose.override.example.yml docker-compose.override.yml +``` + + +Open the `docker-compose.override.yml` file to adjust the database volume path. + +### Provision the services + +In order to provision the services run: + +```bash +docker-compose up -d +``` + + +**Note:** +Make sure to change your database host variable in `.env` and `.env.test` files +to your docker database service name that is in `docker-compose.yml` (e.g. `db`) + +### Dev Containers + +You can work inside a Docker container instead of your host if you like. + +**How to use (VS Code):** + +- Make sure you have installed [Dev Containers Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- Open the project's folder in VS Code +- Hit `Ctrl`/`Cmd` + `Shift` + `P` -> Dev Containers: Open Folder in Container + +For more information on Dev Containers and [supported editors](https://containers.dev/supporting) +you can check the [documentation](https://containers.dev/). + + +## Running the app + +```bash +# build the app +npm run build +# run in development mode +npm run start + +# run in watch mode +npm run start:dev + +# run in debug mode +npm run start:debug +``` + +## Test + +### Unit Tests + +```bash +# run +npm run test + +# watch +npm run test:watch + +# coverage +npm run test:cov +``` + +### End-to-end Tests + +```bash +# e2e tests +npm run test:e2e + +# e2e coverage +npm run test:e2e:cov +``` + +## Working with migrations + +The underlying library is `knex`. You can find their guide on migrations [here](http://knexjs.org/guide/migrations.html). + +```bash +# check what is the last applied migration +npm run db:migrate:version + +# check which migrations have been run and how many are pending +npm run db:migrate:status + +# create a new migration file +npm run db:migrate:make + +# run the next migration that has not yet been run +npm run db:migrate:up + +# undo the last migration that was run +npm run db:migrate:down + +# run all pending migrations +npm run db:migrate:latest + +# rollback the last batch of migrations +npm run db:migrate:rollback + +# rollback all migrations +npm run db:migrate:rollback --all + +# rollback all migrations and re-apply latest +npm run db:migrate:reset +``` + +To debug migrations set the `DEBUG` environment variable to `knex:query`, e.g. + +```bash +DEBUG=knex:query npm run db:migrate:up +``` + +## Working with Docker + +```bash +# build the image +npm run image:build + +# run the image +npm run image:run +``` + + +## Working with OpenAPI + +Swagger Document is being generated when the application is started. +In order to see it you can navigate to `{HOST}:{PORT}/swagger` +e.g. `http://localhost:3000/swagger` (make sure the app is running). + +You can change the route if you want to in `main.ts` + +## Working with Licenses + +Whenever a new `npm` module is added to the project we need to check its +license. If the license is included in the +[OSI approved license list](https://opensource.org/licenses/), then we +can use it. If not - we need to review it manually. Start by running +the following `npm` script: + +```bash +npm run license:check +``` + +If the script succeeds, that means that the license is either a included +in the OSI list, or it was already reviewed and passed the review. When +the script fails it will print the first license that needs a review. +In order to list all licenses that need a review run the following: + +```bash +npm run license:for-review +``` + +In order to verify that a license suits your case, you can use a website like +[SOOS](https://app.soos.io/research/licenses). Find the license and inspect +the `Usage` and `Requirements` sections. You need to verify that the license +is `permissive`. Look for things like whether the license allows for +`Commercial Use` and `Private Use`. + +If the package passes the review, then include it in the list in +`licenses-reviewed.js` located in the root of ths project. + +If the package does not pass the review, then look for an alternative package. + +Lastly, if you just want to print a summary of all license, run: + +```bash +npm run license:summary +``` + +## Debug + +### VS Code + +Go to the Debug menu (CTRL+SHIFT+D). From `RUN AND DEBUG` at the top select +`Run Script: Launch via NPM`. You should now be able to start debugging +by pressing `F5`. + diff --git a/assets/nest/example-app-pg/docker-compose.override.example.yml b/nest-pg-auth0/docker-compose.override.example.yml similarity index 100% rename from assets/nest/example-app-pg/docker-compose.override.example.yml rename to nest-pg-auth0/docker-compose.override.example.yml diff --git a/assets/nest/example-app-pg/docker-compose.yml b/nest-pg-auth0/docker-compose.yml similarity index 100% rename from assets/nest/example-app-pg/docker-compose.yml rename to nest-pg-auth0/docker-compose.yml diff --git a/nest-pg-auth0/jest.config.js b/nest-pg-auth0/jest.config.js new file mode 100644 index 00000000..575c1728 --- /dev/null +++ b/nest-pg-auth0/jest.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + collectCoverageFrom: ['/src/**/*.(t|j)s'], + coverageDirectory: './coverage', + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/src/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*controller.ts$', + '/src/utils/class-transformers/.*', + '/src/database/seeds/.*', + '/src/.*interceptor.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '.', + testEnvironment: 'node', + testRegex: '.*\\.spec\\.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, +}; diff --git a/nest-pg-auth0/jest.setup.ts b/nest-pg-auth0/jest.setup.ts new file mode 100644 index 00000000..4134a28a --- /dev/null +++ b/nest-pg-auth0/jest.setup.ts @@ -0,0 +1,7 @@ +import '@database/extensions/knex/register'; +import 'reflect-metadata'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/nest-pg-auth0/licenses-allowed.js b/nest-pg-auth0/licenses-allowed.js new file mode 100644 index 00000000..2bee46b1 --- /dev/null +++ b/nest-pg-auth0/licenses-allowed.js @@ -0,0 +1,11 @@ +const spdxLicenses = require('spdx-license-list'); + +const separator = process.argv[2]; + +const osiApprovedLicenses = Object.entries(spdxLicenses) + .filter(([, value]) => value.osiApproved) + .map(([k]) => k); + +const reviewedLicenses = require('./licenses-reviewed'); + +console.log(osiApprovedLicenses.concat(reviewedLicenses).join(separator)); diff --git a/nest-pg-auth0/licenses-reviewed.js b/nest-pg-auth0/licenses-reviewed.js new file mode 100644 index 00000000..e7844022 --- /dev/null +++ b/nest-pg-auth0/licenses-reviewed.js @@ -0,0 +1,34 @@ +module.exports = [ + /** + * @link https://creativecommons.org/licenses/by/1.0/ + * + * Dependencies: + * spdx-license-ids + * spdx-license-list + */ + 'CC0-1.0', + + /** + * @link https://creativecommons.org/licenses/by/3.0/ + * + * Dependencies: + * spdx-ranges + */ + 'CC-BY-3.0', + + /** + * @link https://creativecommons.org/licenses/by/4.0/ + * + * Dependencies: + * caniuse-lite + */ + 'CC-BY-4.0', + + /** + * @link https://blueoakcouncil.org/license/1.0.0 + * + * Dependencies: + * markdownlint-cli + */ + 'BlueOak-1.0.0', +]; diff --git a/assets/nest/example-app-pg/migrations/20230302155244_initial.ts b/nest-pg-auth0/migrations/20230302155244_initial.ts similarity index 100% rename from assets/nest/example-app-pg/migrations/20230302155244_initial.ts rename to nest-pg-auth0/migrations/20230302155244_initial.ts diff --git a/assets/nest/example-app-pg/migrations/20230302162022_create_table_users.ts b/nest-pg-auth0/migrations/20230302162022_create_table_users.ts similarity index 100% rename from assets/nest/example-app-pg/migrations/20230302162022_create_table_users.ts rename to nest-pg-auth0/migrations/20230302162022_create_table_users.ts diff --git a/assets/nest/example-app-pg/migrations/20230302162023_create_table_todos.ts b/nest-pg-auth0/migrations/20230302162023_create_table_todos.ts similarity index 100% rename from assets/nest/example-app-pg/migrations/20230302162023_create_table_todos.ts rename to nest-pg-auth0/migrations/20230302162023_create_table_todos.ts diff --git a/assets/nest/example-app-pg/migrations/utils/index.ts b/nest-pg-auth0/migrations/utils/index.ts similarity index 100% rename from assets/nest/example-app-pg/migrations/utils/index.ts rename to nest-pg-auth0/migrations/utils/index.ts diff --git a/assets/nest/ts/nest-cli.json b/nest-pg-auth0/nest-cli.json similarity index 100% rename from assets/nest/ts/nest-cli.json rename to nest-pg-auth0/nest-cli.json diff --git a/nest-pg-auth0/package.json b/nest-pg-auth0/package.json new file mode 100644 index 00000000..febdd5dc --- /dev/null +++ b/nest-pg-auth0/package.json @@ -0,0 +1,109 @@ +{ + "name": "nest-pg-auth0", + "version": "0.0.1", + "private": true, + "description": "", + "license": "UNLICENSED", + "author": "", + "scripts": { + "build": "nest build", + "db:migrate:down": "ts-node node_modules/knex/bin/cli.js migrate:down --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:latest": "ts-node node_modules/knex/bin/cli.js migrate:latest --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:make": "knex migrate:make -x ts --migrations-directory ./migrations", + "db:migrate:reset": "npm run db:migrate:rollback --all && npm run db:migrate:latest", + "db:migrate:rollback": "ts-node node_modules/knex/bin/cli.js migrate:rollback --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:status": "ts-node node_modules/knex/bin/cli.js migrate:status --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:up": "ts-node node_modules/knex/bin/cli.js migrate:up --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:version": "ts-node node_modules/knex/bin/cli.js migrate:currentVersion --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "image:build": "DOCKER_BUILDKIT=1 docker build -t nest-pg-auth0 .", + "image:run": "docker run --rm --net host -e NODE_ENV=production --env-file .env nest-pg-auth0", + "license:check": "license-checker --summary --excludePrivatePackages --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --excludePrivatePackages --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "lint:dockerfile": "docker run --rm -i hadolint/hadolint < Dockerfile", + "lint:markdown": "markdownlint **/*.md --ignore node_modules", + "prepare": "husky install", + "start": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start", + "start:debug": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --debug --watch", + "start:dev": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --watch", + "start:prod": "node dist/main", + "test": "jest", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "npx jest --config ./test/jest-e2e.config.js --runInBand", + "test:e2e:cov": "npm run test:e2e -- --coverage", + "test:watch": "jest --watch" + }, + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/axios": "^3.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/passport": "^10.0.1", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "jwks-rsa": "^3.0.1", + "knex": "^2.4.2", + "passport-jwt": "^4.0.1", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/node": "18.16.12", + "@types/passport-jwt": "^3.0.9", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" + } +} \ No newline at end of file diff --git a/nest-pg-auth0/scripts/db-connection.ts b/nest-pg-auth0/scripts/db-connection.ts new file mode 100644 index 00000000..31acfe3c --- /dev/null +++ b/nest-pg-auth0/scripts/db-connection.ts @@ -0,0 +1,3 @@ +console.log( + `postgres://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}` +); diff --git a/nest-pg-auth0/scripts/detect-secrets.sh b/nest-pg-auth0/scripts/detect-secrets.sh new file mode 100755 index 00000000..27d54428 --- /dev/null +++ b/nest-pg-auth0/scripts/detect-secrets.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +RED=$'\e[0;31m' + +echo "[INFO] Running detect-secrets hook" + +# Exit if docker is not found +if ! command -v docker &> /dev/null +then + echo "${RED}[ERROR] docker could not be found! Docker is required in order to run detect-secrets" + exit 1 +fi + +echo "[INFO] If you encounter any issues with detect-secrets, please refer to https://github.com/Yelp/detect-secrets" + +VOLUME_PATH="--volume $(pwd):/usr/src/app" +EXCLUDED_FILES="--exclude-files .*\.(test|spec|e2e)(-e2e|-spec)?\.(js|ts)$ --exclude-files .github/workflows/coverage-e2e.yaml" +STAGED_FILES_PATHS=$(git diff --staged --name-only -z | xargs -0) + +# shellcheck disable=SC2086 +if ! docker run --rm --name detect-secrets $VOLUME_PATH lirantal/detect-secrets $EXCLUDED_FILES --baseline .secrets.baseline $STAGED_FILES_PATHS; +then + echo "${RED}[ERROR] Remove secrets before commiting." + exit 1 +fi + +echo "[INFO] detect-secrets hook completed succesfully. No secrets found!" \ No newline at end of file diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.controller.ts b/nest-pg-auth0/src/api/auth/auth.controller.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/auth.controller.ts rename to nest-pg-auth0/src/api/auth/auth.controller.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.module.ts b/nest-pg-auth0/src/api/auth/auth.module.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/auth.module.ts rename to nest-pg-auth0/src/api/auth/auth.module.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/credentials.dto.ts b/nest-pg-auth0/src/api/auth/dto/credentials.dto.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/dto/credentials.dto.ts rename to nest-pg-auth0/src/api/auth/dto/credentials.dto.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/index.ts b/nest-pg-auth0/src/api/auth/dto/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/dto/index.ts rename to nest-pg-auth0/src/api/auth/dto/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.spec.ts b/nest-pg-auth0/src/api/auth/guards/auth.guard.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.spec.ts rename to nest-pg-auth0/src/api/auth/guards/auth.guard.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.ts b/nest-pg-auth0/src/api/auth/guards/auth.guard.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.ts rename to nest-pg-auth0/src/api/auth/guards/auth.guard.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/index.ts b/nest-pg-auth0/src/api/auth/guards/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/guards/index.ts rename to nest-pg-auth0/src/api/auth/guards/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/auth.ts b/nest-pg-auth0/src/api/auth/interfaces/auth.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/auth.ts rename to nest-pg-auth0/src/api/auth/interfaces/auth.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/index.ts b/nest-pg-auth0/src/api/auth/interfaces/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/index.ts rename to nest-pg-auth0/src/api/auth/interfaces/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.spec.ts b/nest-pg-auth0/src/api/auth/services/auth.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.spec.ts rename to nest-pg-auth0/src/api/auth/services/auth.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.ts b/nest-pg-auth0/src/api/auth/services/auth.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.ts rename to nest-pg-auth0/src/api/auth/services/auth.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.spec.ts b/nest-pg-auth0/src/api/auth/services/auth0.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.spec.ts rename to nest-pg-auth0/src/api/auth/services/auth0.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.ts b/nest-pg-auth0/src/api/auth/services/auth0.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.ts rename to nest-pg-auth0/src/api/auth/services/auth0.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/index.ts b/nest-pg-auth0/src/api/auth/services/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/index.ts rename to nest-pg-auth0/src/api/auth/services/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.spec.ts b/nest-pg-auth0/src/api/auth/services/jwt.strategy.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.spec.ts rename to nest-pg-auth0/src/api/auth/services/jwt.strategy.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.ts b/nest-pg-auth0/src/api/auth/services/jwt.strategy.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.ts rename to nest-pg-auth0/src/api/auth/services/jwt.strategy.ts diff --git a/nest-pg-auth0/src/api/healthchecks/healthchecks.controller.ts b/nest-pg-auth0/src/api/healthchecks/healthchecks.controller.ts new file mode 100644 index 00000000..d898fd58 --- /dev/null +++ b/nest-pg-auth0/src/api/healthchecks/healthchecks.controller.ts @@ -0,0 +1,50 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '@utils/decorators'; + +@ApiTags('Healthchecks') +@Controller('healthz') +export class HealthchecksController { + @Public() + @Get('live') + @ApiOperation({ + summary: 'Liveness endpoint', + description: 'Used to check whether the application is alive.', + }) + @ApiOkResponse({ + description: 'Application is alive', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + live(): string { + return 'OK'; + } + + @Public() + @Get('ready') + @ApiOperation({ + summary: 'Readiness endpoint', + description: + 'Used to check whether the application is ready to receive requests.', + }) + @ApiOkResponse({ + description: 'Application is ready', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + ready(): string { + return 'OK'; + } +} diff --git a/nest-pg-auth0/src/api/healthchecks/healthchecks.module.ts b/nest-pg-auth0/src/api/healthchecks/healthchecks.module.ts new file mode 100644 index 00000000..797561fc --- /dev/null +++ b/nest-pg-auth0/src/api/healthchecks/healthchecks.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HealthchecksController } from './healthchecks.controller'; + +@Module({ + controllers: [HealthchecksController], +}) +export class HealthchecksModule {} diff --git a/nest-pg-auth0/src/api/todos/__mocks__/index.ts b/nest-pg-auth0/src/api/todos/__mocks__/index.ts new file mode 100644 index 00000000..3f42ef2e --- /dev/null +++ b/nest-pg-auth0/src/api/todos/__mocks__/index.ts @@ -0,0 +1 @@ +export * from './todos.mocks'; diff --git a/assets/nest/example-app-pg/src/api/todos/__mocks__/todos.mocks.ts b/nest-pg-auth0/src/api/todos/__mocks__/todos.mocks.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/__mocks__/todos.mocks.ts rename to nest-pg-auth0/src/api/todos/__mocks__/todos.mocks.ts diff --git a/nest-pg-auth0/src/api/todos/dto/create-todo.dto.ts b/nest-pg-auth0/src/api/todos/dto/create-todo.dto.ts new file mode 100644 index 00000000..c100f414 --- /dev/null +++ b/nest-pg-auth0/src/api/todos/dto/create-todo.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Trim } from '@utils/class-transformers'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; + +export class CreateTodoDto { + @ApiProperty() + @Trim() + @MinLength(1) + @MaxLength(255) + name: string; + + @ApiProperty({ type: String, required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/assets/nest/example-app-pg/src/api/todos/dto/find-all-todos-query.dto.ts b/nest-pg-auth0/src/api/todos/dto/find-all-todos-query.dto.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/dto/find-all-todos-query.dto.ts rename to nest-pg-auth0/src/api/todos/dto/find-all-todos-query.dto.ts diff --git a/nest-pg-auth0/src/api/todos/dto/index.ts b/nest-pg-auth0/src/api/todos/dto/index.ts new file mode 100644 index 00000000..ea59f55f --- /dev/null +++ b/nest-pg-auth0/src/api/todos/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-todo.dto'; +export * from './find-all-todos-query.dto'; +export * from './update-todo.dto'; diff --git a/nest-pg-auth0/src/api/todos/dto/update-todo.dto.ts b/nest-pg-auth0/src/api/todos/dto/update-todo.dto.ts new file mode 100644 index 00000000..e00f0533 --- /dev/null +++ b/nest-pg-auth0/src/api/todos/dto/update-todo.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateTodoDto } from './create-todo.dto'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; +import { Trim } from '@utils/class-transformers'; + +export class UpdateTodoDto extends PartialType(CreateTodoDto) { + @ApiProperty({ required: false }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/nest-pg-auth0/src/api/todos/entities/index.ts b/nest-pg-auth0/src/api/todos/entities/index.ts new file mode 100644 index 00000000..69b0759f --- /dev/null +++ b/nest-pg-auth0/src/api/todos/entities/index.ts @@ -0,0 +1 @@ +export * from './todo.entity'; diff --git a/assets/nest/example-app-pg/src/api/todos/entities/todo.entity.ts b/nest-pg-auth0/src/api/todos/entities/todo.entity.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/entities/todo.entity.ts rename to nest-pg-auth0/src/api/todos/entities/todo.entity.ts diff --git a/nest-pg-auth0/src/api/todos/error-mappings/index.ts b/nest-pg-auth0/src/api/todos/error-mappings/index.ts new file mode 100644 index 00000000..cec33482 --- /dev/null +++ b/nest-pg-auth0/src/api/todos/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './todo-user-not-found.error-mapping'; diff --git a/assets/nest/example-app-pg/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/nest-pg-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts rename to nest-pg-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts diff --git a/nest-pg-auth0/src/api/todos/interfaces/index.ts b/nest-pg-auth0/src/api/todos/interfaces/index.ts new file mode 100644 index 00000000..1faae003 --- /dev/null +++ b/nest-pg-auth0/src/api/todos/interfaces/index.ts @@ -0,0 +1 @@ +export * from './todos.interface'; diff --git a/assets/nest/example-app-pg/src/api/todos/interfaces/todos.interface.ts b/nest-pg-auth0/src/api/todos/interfaces/todos.interface.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/interfaces/todos.interface.ts rename to nest-pg-auth0/src/api/todos/interfaces/todos.interface.ts diff --git a/nest-pg-auth0/src/api/todos/repositories/index.ts b/nest-pg-auth0/src/api/todos/repositories/index.ts new file mode 100644 index 00000000..2639d438 --- /dev/null +++ b/nest-pg-auth0/src/api/todos/repositories/index.ts @@ -0,0 +1 @@ +export * from './todos.repository'; diff --git a/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.spec.ts b/nest-pg-auth0/src/api/todos/repositories/todos.repository.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.spec.ts rename to nest-pg-auth0/src/api/todos/repositories/todos.repository.spec.ts diff --git a/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.ts b/nest-pg-auth0/src/api/todos/repositories/todos.repository.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.ts rename to nest-pg-auth0/src/api/todos/repositories/todos.repository.ts diff --git a/assets/nest/example-app-pg/src/api/todos/todos.controller.ts b/nest-pg-auth0/src/api/todos/todos.controller.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/todos.controller.ts rename to nest-pg-auth0/src/api/todos/todos.controller.ts diff --git a/nest-pg-auth0/src/api/todos/todos.module.ts b/nest-pg-auth0/src/api/todos/todos.module.ts new file mode 100644 index 00000000..20fefaa8 --- /dev/null +++ b/nest-pg-auth0/src/api/todos/todos.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { DatabaseModule } from '@database/database.module'; +import { TodosRepository } from './repositories/todos.repository'; + +@Module({ + imports: [DatabaseModule], + controllers: [TodosController], + providers: [TodosService, TodosRepository], +}) +export class TodosModule {} diff --git a/assets/nest/example-app-pg/src/api/todos/todos.service.spec.ts b/nest-pg-auth0/src/api/todos/todos.service.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/todos.service.spec.ts rename to nest-pg-auth0/src/api/todos/todos.service.spec.ts diff --git a/assets/nest/example-app-pg/src/api/todos/todos.service.ts b/nest-pg-auth0/src/api/todos/todos.service.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/todos/todos.service.ts rename to nest-pg-auth0/src/api/todos/todos.service.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/index.ts b/nest-pg-auth0/src/api/users/entities/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/entities/index.ts rename to nest-pg-auth0/src/api/users/entities/index.ts diff --git a/assets/nest/example-app-pg/src/api/users/entities/user.entity.ts b/nest-pg-auth0/src/api/users/entities/user.entity.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/entities/user.entity.ts rename to nest-pg-auth0/src/api/users/entities/user.entity.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/index.ts b/nest-pg-auth0/src/api/users/error-mappings/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/index.ts rename to nest-pg-auth0/src/api/users/error-mappings/index.ts diff --git a/assets/nest/example-app-pg/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/nest-pg-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/error-mappings/user-email-taken.error-mapping.ts rename to nest-pg-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/index.ts b/nest-pg-auth0/src/api/users/repositories/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/index.ts rename to nest-pg-auth0/src/api/users/repositories/index.ts diff --git a/assets/nest/example-app-pg/src/api/users/repositories/users.repository.spec.ts b/nest-pg-auth0/src/api/users/repositories/users.repository.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/repositories/users.repository.spec.ts rename to nest-pg-auth0/src/api/users/repositories/users.repository.spec.ts diff --git a/assets/nest/example-app-pg/src/api/users/repositories/users.repository.ts b/nest-pg-auth0/src/api/users/repositories/users.repository.ts similarity index 100% rename from assets/nest/example-app-pg/src/api/users/repositories/users.repository.ts rename to nest-pg-auth0/src/api/users/repositories/users.repository.ts diff --git a/assets/nest/multiple-choice-features/users/mongodb/jwt/users.module.ts b/nest-pg-auth0/src/api/users/users.module.ts similarity index 100% rename from assets/nest/multiple-choice-features/users/mongodb/jwt/users.module.ts rename to nest-pg-auth0/src/api/users/users.module.ts diff --git a/nest-pg-auth0/src/app.module.ts b/nest-pg-auth0/src/app.module.ts new file mode 100644 index 00000000..6f383b64 --- /dev/null +++ b/nest-pg-auth0/src/app.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DatabaseModule } from '@database/database.module'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; +import { AuthModule } from '@api/auth/auth.module'; +import { UsersModule } from '@api/users/users.module'; +import { TodosModule } from '@api/todos/todos.module'; +import { nodeConfig, dbConfig, authConfig } from '@utils/environment'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + cache: true, + ignoreEnvFile: true, + isGlobal: true, + }), + DatabaseModule, + HealthchecksModule, + UsersModule, + AuthModule, + TodosModule, + ], +}) +export class AppModule {} diff --git a/assets/nest/example-app-pg/src/database/base-repository.repository.spec.ts b/nest-pg-auth0/src/database/base-repository.repository.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/base-repository.repository.spec.ts rename to nest-pg-auth0/src/database/base-repository.repository.spec.ts diff --git a/assets/nest/example-app-pg/src/database/base-repository.repository.ts b/nest-pg-auth0/src/database/base-repository.repository.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/base-repository.repository.ts rename to nest-pg-auth0/src/database/base-repository.repository.ts diff --git a/assets/nest/example-app-pg/src/database/constants.ts b/nest-pg-auth0/src/database/constants.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/constants.ts rename to nest-pg-auth0/src/database/constants.ts diff --git a/assets/nest/example-app-pg/src/database/database.module.ts b/nest-pg-auth0/src/database/database.module.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/database.module.ts rename to nest-pg-auth0/src/database/database.module.ts diff --git a/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts b/nest-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts rename to nest-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts diff --git a/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/nest-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.ts rename to nest-pg-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts diff --git a/assets/nest/example-app-pg/src/database/error-mappings/index.ts b/nest-pg-auth0/src/database/error-mappings/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/error-mappings/index.ts rename to nest-pg-auth0/src/database/error-mappings/index.ts diff --git a/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.spec.ts b/nest-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.spec.ts rename to nest-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.spec.ts diff --git a/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.ts b/nest-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.ts rename to nest-pg-auth0/src/database/error-mappings/unique-violation.error-mapping.ts diff --git a/nest-pg-auth0/src/database/errors/duplicate-record.error.ts b/nest-pg-auth0/src/database/errors/duplicate-record.error.ts new file mode 100644 index 00000000..65fbab07 --- /dev/null +++ b/nest-pg-auth0/src/database/errors/duplicate-record.error.ts @@ -0,0 +1,6 @@ +export class DuplicateRecordError extends Error { + constructor(message: string) { + super(message); + this.name = DuplicateRecordError.name; + } +} diff --git a/nest-pg-auth0/src/database/errors/index.ts b/nest-pg-auth0/src/database/errors/index.ts new file mode 100644 index 00000000..7099d226 --- /dev/null +++ b/nest-pg-auth0/src/database/errors/index.ts @@ -0,0 +1,2 @@ +export * from './duplicate-record.error'; +export * from './record-not-found.error'; diff --git a/nest-pg-auth0/src/database/errors/record-not-found.error.ts b/nest-pg-auth0/src/database/errors/record-not-found.error.ts new file mode 100644 index 00000000..52bcb4fa --- /dev/null +++ b/nest-pg-auth0/src/database/errors/record-not-found.error.ts @@ -0,0 +1,6 @@ +export class RecordNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = RecordNotFoundError.name; + } +} diff --git a/assets/nest/example-app-pg/src/database/extensions/knex/index.ts b/nest-pg-auth0/src/database/extensions/knex/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/extensions/knex/index.ts rename to nest-pg-auth0/src/database/extensions/knex/index.ts diff --git a/assets/nest/example-app-pg/src/database/extensions/knex/register.ts b/nest-pg-auth0/src/database/extensions/knex/register.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/extensions/knex/register.ts rename to nest-pg-auth0/src/database/extensions/knex/register.ts diff --git a/assets/nest/example-app-pg/src/database/nest-knex.service.ts b/nest-pg-auth0/src/database/nest-knex.service.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/nest-knex.service.ts rename to nest-pg-auth0/src/database/nest-knex.service.ts diff --git a/assets/nest/example-app-pg/src/database/query-builder/extensions.spec.ts b/nest-pg-auth0/src/database/query-builder/extensions.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/query-builder/extensions.spec.ts rename to nest-pg-auth0/src/database/query-builder/extensions.spec.ts diff --git a/assets/nest/example-app-pg/src/database/query-builder/extensions.ts b/nest-pg-auth0/src/database/query-builder/extensions.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/query-builder/extensions.ts rename to nest-pg-auth0/src/database/query-builder/extensions.ts diff --git a/assets/nest/example-app-pg/src/database/query-builder/index.ts b/nest-pg-auth0/src/database/query-builder/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/query-builder/index.ts rename to nest-pg-auth0/src/database/query-builder/index.ts diff --git a/assets/nest/example-app-pg/src/database/seeds/test/index.js b/nest-pg-auth0/src/database/seeds/test/index.js similarity index 100% rename from assets/nest/example-app-pg/src/database/seeds/test/index.js rename to nest-pg-auth0/src/database/seeds/test/index.js diff --git a/assets/nest/example-app-pg/src/database/utils/error.ts b/nest-pg-auth0/src/database/utils/error.ts similarity index 100% rename from assets/nest/example-app-pg/src/database/utils/error.ts rename to nest-pg-auth0/src/database/utils/error.ts diff --git a/nest-pg-auth0/src/database/utils/index.ts b/nest-pg-auth0/src/database/utils/index.ts new file mode 100644 index 00000000..93ae819e --- /dev/null +++ b/nest-pg-auth0/src/database/utils/index.ts @@ -0,0 +1 @@ +export * from './error'; diff --git a/assets/nest/example-app-pg/src/main.ts b/nest-pg-auth0/src/main.ts similarity index 100% rename from assets/nest/example-app-pg/src/main.ts rename to nest-pg-auth0/src/main.ts diff --git a/nest-pg-auth0/src/utils/api/index.ts b/nest-pg-auth0/src/utils/api/index.ts new file mode 100644 index 00000000..dbc1ea0f --- /dev/null +++ b/nest-pg-auth0/src/utils/api/index.ts @@ -0,0 +1 @@ +export * from './response'; diff --git a/assets/nest/example-app-pg/src/utils/api/response.ts b/nest-pg-auth0/src/utils/api/response.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/api/response.ts rename to nest-pg-auth0/src/utils/api/response.ts diff --git a/assets/nest/example-app-pg/src/utils/class-transformers/index.ts b/nest-pg-auth0/src/utils/class-transformers/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/class-transformers/index.ts rename to nest-pg-auth0/src/utils/class-transformers/index.ts diff --git a/assets/nest/example-app-pg/src/utils/class-transformers/lower-case.ts b/nest-pg-auth0/src/utils/class-transformers/lower-case.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/class-transformers/lower-case.ts rename to nest-pg-auth0/src/utils/class-transformers/lower-case.ts diff --git a/nest-pg-auth0/src/utils/class-transformers/trim.ts b/nest-pg-auth0/src/utils/class-transformers/trim.ts new file mode 100644 index 00000000..b261cbce --- /dev/null +++ b/nest-pg-auth0/src/utils/class-transformers/trim.ts @@ -0,0 +1,4 @@ +import { Transform } from 'class-transformer'; + +export const Trim = () => + Transform((params) => params.value?.trim(), { toClassOnly: true }); diff --git a/nest-pg-auth0/src/utils/decorators/index.ts b/nest-pg-auth0/src/utils/decorators/index.ts new file mode 100644 index 00000000..3f75d993 --- /dev/null +++ b/nest-pg-auth0/src/utils/decorators/index.ts @@ -0,0 +1 @@ +export * from './public.decorator'; diff --git a/nest-pg-auth0/src/utils/decorators/public.decorator.spec.ts b/nest-pg-auth0/src/utils/decorators/public.decorator.spec.ts new file mode 100644 index 00000000..b4aab11b --- /dev/null +++ b/nest-pg-auth0/src/utils/decorators/public.decorator.spec.ts @@ -0,0 +1,20 @@ +import { IS_PUBLIC_KEY, Public } from './public.decorator'; + +describe('@Public', () => { + @Public() + class PublicRoutes {} + + class GuardedRoutes {} + + it('should be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, PublicRoutes); + + expect(isPublic).toBe(true); + }); + + it('should not be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, GuardedRoutes); + + expect(isPublic).toBe(undefined); + }); +}); diff --git a/nest-pg-auth0/src/utils/decorators/public.decorator.ts b/nest-pg-auth0/src/utils/decorators/public.decorator.ts new file mode 100644 index 00000000..b3845e12 --- /dev/null +++ b/nest-pg-auth0/src/utils/decorators/public.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/nest-pg-auth0/src/utils/dtos/errors.dto.ts b/nest-pg-auth0/src/utils/dtos/errors.dto.ts new file mode 100644 index 00000000..661bfa22 --- /dev/null +++ b/nest-pg-auth0/src/utils/dtos/errors.dto.ts @@ -0,0 +1,63 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Errors } from '@utils/enums'; + +interface ErrorDto { + statusCode: number; + message: string; + error: string; +} + +export class BadRequestDto implements ErrorDto { + @ApiProperty({ example: 400 }) + statusCode: number; + + @ApiProperty({ example: Errors.BadRequest }) + message: string; + + @ApiProperty({ example: Errors.BadRequest }) + error: string; +} + +export class UnauthorizedDto implements ErrorDto { + @ApiProperty({ example: 401 }) + statusCode: number; + + @ApiProperty({ example: Errors.Unauthorized }) + message: string; + + @ApiProperty({ example: Errors.Unauthorized }) + error: string; +} + +export class NotFoundDto implements ErrorDto { + @ApiProperty({ example: 404 }) + statusCode: number; + + @ApiProperty({ example: 'Record not found' }) + message: string; + + @ApiProperty({ example: Errors.NotFound }) + error: string; +} + +export class ConflictDto implements ErrorDto { + @ApiProperty({ example: 409 }) + statusCode: number; + + @ApiProperty({ example: 'Record already exists' }) + message: string; + + @ApiProperty({ example: Errors.Conflict }) + error: string; +} + +export class UnprocessableEntityDto implements ErrorDto { + @ApiProperty({ example: 422 }) + statusCode: number; + + @ApiProperty({ example: 'Invalid input' }) + message: string; + + @ApiProperty({ example: Errors.UnprocessableEntity }) + error: string; +} diff --git a/nest-pg-auth0/src/utils/dtos/index.ts b/nest-pg-auth0/src/utils/dtos/index.ts new file mode 100644 index 00000000..1a520955 --- /dev/null +++ b/nest-pg-auth0/src/utils/dtos/index.ts @@ -0,0 +1,2 @@ +export * from './pagination.dto'; +export * from './errors.dto'; diff --git a/nest-pg-auth0/src/utils/dtos/pagination.dto.ts b/nest-pg-auth0/src/utils/dtos/pagination.dto.ts new file mode 100644 index 00000000..0bdf43ae --- /dev/null +++ b/nest-pg-auth0/src/utils/dtos/pagination.dto.ts @@ -0,0 +1,14 @@ +import { Transform } from 'class-transformer'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class PaginationDto { + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number; +} diff --git a/assets/nest/example-app-pg/src/utils/entities/generic-entity.ts b/nest-pg-auth0/src/utils/entities/generic-entity.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/entities/generic-entity.ts rename to nest-pg-auth0/src/utils/entities/generic-entity.ts diff --git a/nest-pg-auth0/src/utils/entities/index.ts b/nest-pg-auth0/src/utils/entities/index.ts new file mode 100644 index 00000000..1fdd9774 --- /dev/null +++ b/nest-pg-auth0/src/utils/entities/index.ts @@ -0,0 +1 @@ +export * from './generic-entity'; diff --git a/nest-pg-auth0/src/utils/enums/errors.enum.ts b/nest-pg-auth0/src/utils/enums/errors.enum.ts new file mode 100644 index 00000000..8d775147 --- /dev/null +++ b/nest-pg-auth0/src/utils/enums/errors.enum.ts @@ -0,0 +1,7 @@ +export enum Errors { + BadRequest = 'Bad Request', + Unauthorized = 'Unauthorized', + NotFound = 'Not Found', + Conflict = 'Conflict', + UnprocessableEntity = 'Unprocessable Entity', +} diff --git a/nest-pg-auth0/src/utils/enums/index.ts b/nest-pg-auth0/src/utils/enums/index.ts new file mode 100644 index 00000000..efe62e58 --- /dev/null +++ b/nest-pg-auth0/src/utils/enums/index.ts @@ -0,0 +1 @@ +export * from './errors.enum'; diff --git a/assets/nest/multiple-choice-features/environment/pg-auth0/environment.spec.ts b/nest-pg-auth0/src/utils/environment.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/pg-auth0/environment.spec.ts rename to nest-pg-auth0/src/utils/environment.spec.ts diff --git a/assets/nest/multiple-choice-features/environment/pg-auth0/environment.ts b/nest-pg-auth0/src/utils/environment.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/pg-auth0/environment.ts rename to nest-pg-auth0/src/utils/environment.ts diff --git a/nest-pg-auth0/src/utils/error-logging.filter.spec.ts b/nest-pg-auth0/src/utils/error-logging.filter.spec.ts new file mode 100644 index 00000000..8b6e8362 --- /dev/null +++ b/nest-pg-auth0/src/utils/error-logging.filter.spec.ts @@ -0,0 +1,68 @@ +import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core'; +import { + ArgumentsHost, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { ErrorLoggingFilter } from './error-logging.filter'; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +jest.spyOn(Logger.prototype, 'error').mockImplementation(() => {}); + +describe('Error logging filter', () => { + let httpAdapterHost: HttpAdapterHost; + let errorLoggingFilter: ErrorLoggingFilter; + const mockStatus = jest.fn(); + const mockGetResponse = jest.fn().mockImplementation(() => ({ + status: mockStatus, + })); + const mockHttpArgumentsHost = jest.fn().mockImplementation(() => ({ + getResponse: mockGetResponse, + })); + const mockArgumentsHost = { + switchToHttp: mockHttpArgumentsHost, + getArgByIndex: jest.fn(), + getArgs: jest.fn(), + getType: jest.fn(), + switchToRpc: jest.fn(), + switchToWs: jest.fn(), + }; + beforeEach(() => { + httpAdapterHost = new HttpAdapterHost(); + httpAdapterHost.httpAdapter = { + reply: jest.fn(), + } as any; + errorLoggingFilter = new ErrorLoggingFilter(httpAdapterHost); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('returns status 500 and logs the error when it is not a generic HTTP exception', () => { + const err = new Error('Generic'); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith(err); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + err, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + }); + + it('returns the status of the HTTP exception and logs the stringified error', () => { + const err = new HttpException('Exception', HttpStatus.BAD_REQUEST); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith('"Exception"'); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + 'Exception', + HttpStatus.BAD_REQUEST, + ); + }); +}); diff --git a/nest-pg-auth0/src/utils/error-logging.filter.ts b/nest-pg-auth0/src/utils/error-logging.filter.ts new file mode 100644 index 00000000..3371caf5 --- /dev/null +++ b/nest-pg-auth0/src/utils/error-logging.filter.ts @@ -0,0 +1,37 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { HttpAdapterHost } from '@nestjs/core'; + +@Catch() +export class ErrorLoggingFilter implements ExceptionFilter { + public logger = new Logger('ErrorLoggingFilter'); + + constructor(private readonly httpAdapterHost: HttpAdapterHost) {} + + public catch(exception: unknown, host: ArgumentsHost): void { + const { httpAdapter } = this.httpAdapterHost; + const ctx = host.switchToHttp(); + + const httpStatus = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + if (exception instanceof HttpException) { + const error = exception.getResponse(); + this.logger.error(JSON.stringify(error)); + + return httpAdapter.reply(ctx.getResponse(), error, httpStatus); + } + + this.logger.error(exception); + + return httpAdapter.reply(ctx.getResponse(), exception, httpStatus); + } +} diff --git a/assets/nest/example-app-pg/src/utils/error.spec.ts b/nest-pg-auth0/src/utils/error.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/error.spec.ts rename to nest-pg-auth0/src/utils/error.spec.ts diff --git a/nest-pg-auth0/src/utils/error.ts b/nest-pg-auth0/src/utils/error.ts new file mode 100644 index 00000000..df376a09 --- /dev/null +++ b/nest-pg-auth0/src/utils/error.ts @@ -0,0 +1,28 @@ +type IsErrorPredicate = (error: unknown) => boolean; + +type ErrorFactory = () => Error; + +export interface ErrorMapping { + isError: IsErrorPredicate; + newError: ErrorFactory; +} + +export const mapError = function (...mappings: ErrorMapping[]) { + return function (err: Error) { + for (const { isError, newError } of mappings) { + if (isError(err)) { + return newError(); + } + } + + return err; + }; +}; + +export const rethrowError = function (...mappings: ErrorMapping[]) { + const errorMapper = mapError(...mappings); + + return function (err: Error) { + throw errorMapper(err); + }; +}; diff --git a/nest-pg-auth0/src/utils/interceptors/index.ts b/nest-pg-auth0/src/utils/interceptors/index.ts new file mode 100644 index 00000000..9841f297 --- /dev/null +++ b/nest-pg-auth0/src/utils/interceptors/index.ts @@ -0,0 +1 @@ +export * from './service-to-http-error.interceptor'; diff --git a/nest-pg-auth0/src/utils/interceptors/request-logging.interceptor.ts b/nest-pg-auth0/src/utils/interceptors/request-logging.interceptor.ts new file mode 100644 index 00000000..b8d9197b --- /dev/null +++ b/nest-pg-auth0/src/utils/interceptors/request-logging.interceptor.ts @@ -0,0 +1,83 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, + Logger, +} from '@nestjs/common'; +import { catchError, Observable, tap, throwError } from 'rxjs'; + +const REMOVED = '[[REMOVED]]'; + +@Injectable() +export class RequestLoggingInterceptor implements NestInterceptor { + private logger = new Logger('RequestLoggingInterceptor'); + + private sanitizeHeaders(headers: Record): Record { + const sanitizedHeaders = { ...headers }; + + if (sanitizedHeaders.Authorization) { + sanitizedHeaders.Authorization = REMOVED; + } + + if (sanitizedHeaders.Cookie) { + sanitizedHeaders.Cookie = REMOVED; + } + + return sanitizedHeaders; + } + + private sanitizeBody(body: Record): Record { + const sanitizedBody = { ...body }; + + if (sanitizedBody.password) { + sanitizedBody.password = REMOVED; + } + + return sanitizedBody; + } + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const timestamp = new Date().toISOString(); + const startTime = process.hrtime(); + + const { url, body, method, headers, ip } = context + .switchToHttp() + .getRequest(); + + return next.handle().pipe( + tap(() => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + }; + + this.logger.log(JSON.stringify(logMsg)); + }), + catchError((err: any) => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + err, + }; + + this.logger.log(JSON.stringify(logMsg)); + return throwError(() => err); + }), + ); + } +} diff --git a/assets/nest/example-app-pg/src/utils/interceptors/service-to-http-error.interceptor.ts b/nest-pg-auth0/src/utils/interceptors/service-to-http-error.interceptor.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/interceptors/service-to-http-error.interceptor.ts rename to nest-pg-auth0/src/utils/interceptors/service-to-http-error.interceptor.ts diff --git a/assets/nest/example-app-pg/src/utils/query/error.spec.ts b/nest-pg-auth0/src/utils/query/error.spec.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/error.spec.ts rename to nest-pg-auth0/src/utils/query/error.spec.ts diff --git a/assets/nest/example-app-pg/src/utils/query/error.ts b/nest-pg-auth0/src/utils/query/error.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/error.ts rename to nest-pg-auth0/src/utils/query/error.ts diff --git a/assets/nest/example-app-pg/src/utils/query/filtration.ts b/nest-pg-auth0/src/utils/query/filtration.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/filtration.ts rename to nest-pg-auth0/src/utils/query/filtration.ts diff --git a/assets/nest/example-app-pg/src/utils/query/index.ts b/nest-pg-auth0/src/utils/query/index.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/index.ts rename to nest-pg-auth0/src/utils/query/index.ts diff --git a/nest-pg-auth0/src/utils/query/pagination.spec.ts b/nest-pg-auth0/src/utils/query/pagination.spec.ts new file mode 100644 index 00000000..378c4a4d --- /dev/null +++ b/nest-pg-auth0/src/utils/query/pagination.spec.ts @@ -0,0 +1,24 @@ +import { + Pagination, + extractPagination, + paginationDefaults, +} from './pagination'; + +describe('extractPagination', () => { + it('should return the provided pagination', () => { + const pagination: Pagination = { pageNumber: 1, pageSize: 10 }; + expect(extractPagination(pagination)).toEqual(pagination); + }); + + it('should supply the defaults for any missing pagination properties', () => { + expect(extractPagination()).toEqual(paginationDefaults); + expect(extractPagination({ pageNumber: 1 })).toEqual({ + ...paginationDefaults, + pageNumber: 1, + }); + expect(extractPagination({ pageSize: 10 })).toEqual({ + ...paginationDefaults, + pageSize: 10, + }); + }); +}); diff --git a/nest-pg-auth0/src/utils/query/pagination.ts b/nest-pg-auth0/src/utils/query/pagination.ts new file mode 100644 index 00000000..a5e5d6e8 --- /dev/null +++ b/nest-pg-auth0/src/utils/query/pagination.ts @@ -0,0 +1,24 @@ +export const paginationDefaults = { + pageNumber: 1, + pageSize: 20, +}; + +export interface Pagination { + pageNumber?: number; + pageSize?: number; +} + +interface PaginationResponse { + totalPages: number; + currentPage: number; +} + +export interface Paginated extends PaginationResponse { + items: Entity[]; + total: number; +} + +export const extractPagination = (pagination?: Pagination) => ({ + ...paginationDefaults, + ...pagination, +}); diff --git a/assets/nest/example-app-pg/src/utils/query/sorting.ts b/nest-pg-auth0/src/utils/query/sorting.ts similarity index 100% rename from assets/nest/example-app-pg/src/utils/query/sorting.ts rename to nest-pg-auth0/src/utils/query/sorting.ts diff --git a/nest-pg-auth0/test/healthchecks.e2e-spec.ts b/nest-pg-auth0/test/healthchecks.e2e-spec.ts new file mode 100644 index 00000000..ed67bfa2 --- /dev/null +++ b/nest-pg-auth0/test/healthchecks.e2e-spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; + +describe('GET /healthz', () => { + let app: NestFastifyApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [HealthchecksModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + + it('/live', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/live', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); + + it('/ready', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/ready', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); +}); diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/jest-e2e.config.js b/nest-pg-auth0/test/jest-e2e.config.js similarity index 80% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/jest-e2e.config.js rename to nest-pg-auth0/test/jest-e2e.config.js index bbf2b554..bc876b71 100644 --- a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/jest-e2e.config.js +++ b/nest-pg-auth0/test/jest-e2e.config.js @@ -2,21 +2,15 @@ * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - moduleFileExtensions: [ - 'js', - 'json', - 'ts' - ], + moduleFileExtensions: ['js', 'json', 'ts'], rootDir: '..', testEnvironment: 'node', testRegex: '.e2e-spec.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest' + '^.+\\.(t|j)s$': 'ts-jest', }, coverageDirectory: 'coverage-e2e', - collectCoverageFrom: [ - '/src/api/**/*.(t|j)s' - ], + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], coveragePathIgnorePatterns: [ '/src/main.ts$', '/.*spec.ts$', @@ -33,12 +27,10 @@ module.exports = { branches: 85, functions: 85, lines: 85, - statements: 85 - } + statements: 85, + }, }, - setupFiles: [ - '/jest.setup.ts' - ], + setupFiles: ['/jest.setup.ts'], // path aliases from tsconfig.json moduleNameMapper: { '^@api/(.*)$': '/src/api/$1', @@ -48,4 +40,4 @@ module.exports = { '^@middleware$': '/src/middleware', '^@utils/(.*)$': '/src/utils/$1', }, -} +}; diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/create.e2e-spec.ts b/nest-pg-auth0/test/todos/create.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/create.e2e-spec.ts rename to nest-pg-auth0/test/todos/create.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-all.e2e-spec.ts b/nest-pg-auth0/test/todos/find-all.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-all.e2e-spec.ts rename to nest-pg-auth0/test/todos/find-all.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-one.e2e-spec.ts b/nest-pg-auth0/test/todos/find-one.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-one.e2e-spec.ts rename to nest-pg-auth0/test/todos/find-one.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/remove.e2e-spec.ts b/nest-pg-auth0/test/todos/remove.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/remove.e2e-spec.ts rename to nest-pg-auth0/test/todos/remove.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/update.e2e-spec.ts b/nest-pg-auth0/test/todos/update.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/update.e2e-spec.ts rename to nest-pg-auth0/test/todos/update.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/get-todo-payload.ts b/nest-pg-auth0/test/todos/utils/get-todo-payload.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/get-todo-payload.ts rename to nest-pg-auth0/test/todos/utils/get-todo-payload.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/index.ts b/nest-pg-auth0/test/todos/utils/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/index.ts rename to nest-pg-auth0/test/todos/utils/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/sortby-field-todos.ts b/nest-pg-auth0/test/todos/utils/sortby-field-todos.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/sortby-field-todos.ts rename to nest-pg-auth0/test/todos/utils/sortby-field-todos.ts diff --git a/assets/nest/example-app-pg/test/utils/expect-error.ts b/nest-pg-auth0/test/utils/expect-error.ts similarity index 100% rename from assets/nest/example-app-pg/test/utils/expect-error.ts rename to nest-pg-auth0/test/utils/expect-error.ts diff --git a/assets/nest/example-app-pg/test/utils/index.ts b/nest-pg-auth0/test/utils/index.ts similarity index 100% rename from assets/nest/example-app-pg/test/utils/index.ts rename to nest-pg-auth0/test/utils/index.ts diff --git a/nest-pg-auth0/tsconfig.build.json b/nest-pg-auth0/tsconfig.build.json new file mode 100644 index 00000000..f54d2265 --- /dev/null +++ b/nest-pg-auth0/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + "test", + "dist", + "**/*spec.ts", + "**/__mocks__" + ] +} diff --git a/assets/nest/example-app-pg/tsconfig.json b/nest-pg-auth0/tsconfig.json similarity index 100% rename from assets/nest/example-app-pg/tsconfig.json rename to nest-pg-auth0/tsconfig.json diff --git a/nest-pg-jwt/.commitlintrc.js b/nest-pg-jwt/.commitlintrc.js new file mode 100755 index 00000000..b0f2e40c --- /dev/null +++ b/nest-pg-jwt/.commitlintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {}, + parserPreset: { + parserOpts: { + referenceActions: null, + issuePrefixes: ['AB#'], + }, + }, +}; diff --git a/nest-pg-jwt/.czrc b/nest-pg-jwt/.czrc new file mode 100644 index 00000000..64b8fecd --- /dev/null +++ b/nest-pg-jwt/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/nest-pg-jwt/.devcontainer/devcontainer.json b/nest-pg-jwt/.devcontainer/devcontainer.json new file mode 100644 index 00000000..3ff38d20 --- /dev/null +++ b/nest-pg-jwt/.devcontainer/devcontainer.json @@ -0,0 +1,9 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Nest API DevContainer", + "dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"], + "service": "api", + "workspaceFolder": "/usr/src/app", + "remoteUser": "root" +} diff --git a/nest-pg-jwt/.devcontainer/docker-compose.yml b/nest-pg-jwt/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..b01c619a --- /dev/null +++ b/nest-pg-jwt/.devcontainer/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + api: + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" diff --git a/nest-pg-jwt/.dockerignore b/nest-pg-jwt/.dockerignore new file mode 100644 index 00000000..24da266d --- /dev/null +++ b/nest-pg-jwt/.dockerignore @@ -0,0 +1,15 @@ +.env* +.eslint* +.git +.husky +.nvmrc +.prettier* +.vscode +coverage +coverage-e2e +dist +docker-compose* +Dockerfile +node_modules +README.md +test diff --git a/nest-pg-jwt/.editorconfig b/nest-pg-jwt/.editorconfig new file mode 100644 index 00000000..5e1950da --- /dev/null +++ b/nest-pg-jwt/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/nest-pg-jwt/.env.example b/nest-pg-jwt/.env.example new file mode 100644 index 00000000..6ee303d1 --- /dev/null +++ b/nest-pg-jwt/.env.example @@ -0,0 +1,27 @@ +# Node +NODE_ENV=development + +# HTTP +PORT=3000 + +# Logging +ERROR_LOGGING=true +REQUEST_LOGGING=true + +# Swagger +SWAGGER=true + +# Knex +DEBUG=knex:query + +# PostgreSQL +PGHOST=localhost +PGPORT=5432 +PGUSER=user +PGPASSWORD=password +PGDATABASE=default + +# JWT +JWT_SECRET=super-secret +JWT_EXPIRATION=7200 + diff --git a/nest-pg-jwt/.eslintignore b/nest-pg-jwt/.eslintignore new file mode 100755 index 00000000..0589ba50 --- /dev/null +++ b/nest-pg-jwt/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.vscode +coverage +dist/ +*.config.js +*rc.js +src/extensions/**/*.js +licenses-allowed.js diff --git a/nest-pg-jwt/.eslintrc.js b/nest-pg-jwt/.eslintrc.js new file mode 100644 index 00000000..259de13c --- /dev/null +++ b/nest-pg-jwt/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + 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/nest-pg-jwt/.github/workflows/audit.yaml b/nest-pg-jwt/.github/workflows/audit.yaml new file mode 100644 index 00000000..d631240e --- /dev/null +++ b/nest-pg-jwt/.github/workflows/audit.yaml @@ -0,0 +1,16 @@ +name: audit + +on: + pull_request: + branches: + - main + +jobs: + audit: + name: Audit packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run audit + run: npm audit --omit=dev diff --git a/nest-pg-jwt/.github/workflows/coverage-e2e.yaml b/nest-pg-jwt/.github/workflows/coverage-e2e.yaml new file mode 100644 index 00000000..599f71aa --- /dev/null +++ b/nest-pg-jwt/.github/workflows/coverage-e2e.yaml @@ -0,0 +1,42 @@ +name: coverage-e2e + +on: + pull_request: + branches: + - main + +env: + NODE_ENV: test + DEBUG: '' + PORT: 3000 + PGHOST: localhost + PGPORT: 5432 + PGUSER: user + PGPASSWORD: password + PGDATABASE: tests + JWT_SECRET: super-secret + JWT_EXPIRATION: 7200 + +jobs: + coverage-e2e: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Create database + uses: isbang/compose-action@v1.4.1 + - name: Run coverage + uses: ArtiomTr/jest-coverage-report-action@v2 + with: + test-script: npm run test:e2e diff --git a/nest-pg-jwt/.github/workflows/coverage.yaml b/nest-pg-jwt/.github/workflows/coverage.yaml new file mode 100644 index 00000000..8de35d91 --- /dev/null +++ b/nest-pg-jwt/.github/workflows/coverage.yaml @@ -0,0 +1,26 @@ +name: coverage + +on: + pull_request: + branches: + - main + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Get test coverage + uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/nest-pg-jwt/.gitignore b/nest-pg-jwt/.gitignore new file mode 100644 index 00000000..008b70cd --- /dev/null +++ b/nest-pg-jwt/.gitignore @@ -0,0 +1,156 @@ +# This is a slightly modified version of https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# OS +.DS_Store + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage/ +coverage-e2e/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional prettier cache +.prettiercache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +# .env +.env* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Docker compose +docker-compose.override.yml + +# 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 diff --git a/nest-pg-jwt/.husky/.gitignore b/nest-pg-jwt/.husky/.gitignore new file mode 100644 index 00000000..c9cdc63b --- /dev/null +++ b/nest-pg-jwt/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/nest-pg-jwt/.husky/commit-msg b/nest-pg-jwt/.husky/commit-msg new file mode 100755 index 00000000..9e8dfba1 --- /dev/null +++ b/nest-pg-jwt/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit $1 diff --git a/nest-pg-jwt/.husky/pre-commit b/nest-pg-jwt/.husky/pre-commit new file mode 100755 index 00000000..2151bed3 --- /dev/null +++ b/nest-pg-jwt/.husky/pre-commit @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- tsc --noEmit +npx --no -- lint-staged +npx --no -- ls-lint +npm run lint:dockerfile +npm run license:check +npm run test:cov +npm run test:e2e:cov +scripts/detect-secrets.sh + +# Enable branch naming validation by uncommenting the lines below and adjust valid_branch_regex to your preference +# # Enforce branch naming conventions +# LC_ALL=C + +# # Git branch will return an empty line before the initial commit +# git_branch=$(git branch) + +# if [ -z "$git_branch" ]; then +# echo "Initial commit, no branch name linting" +# exit 0 +# fi + +# # INSERT A REGEX PATTERN ACCORDING TO YOUR PROJECT BRANCH NAMING CONVENTION. +# branch_regex="^(chore|feat|fix|docs|style|refactor|test|revert|ci)\/[a-z0-9._-]+$" + +# # Get the branch name +# branch="$(git rev-parse --abbrev-ref HEAD)" + +# # UPDATE THE EXAMPLES TO MATCH YOUR PROJECT BRANCH NAMING CONVENTION. +# error_message="Branch name validation failed. Branch name must adhere to: /$branch_regex/. +# You should rename your branch to a valid name and try again. Here are some examples of valid branch names: + +# feat/my-feature +# test/my-feature +# fix/that-bug +# chore/my-task +# docs/update-readme +# refactor/do-thing-differently +# ci/update-build-script +# " + + +# # Use grep to do the matching, as `if [[ ]]` is not available in `sh` +# match=$(echo $branch | grep -E "$branch_regex") || echo "" + +# # Check if the match is a non-empty string +# if [ -z "$match" ]; then +# echo "$error_message" +# exit 1 +# fi diff --git a/nest-pg-jwt/.husky/pre-push b/nest-pg-jwt/.husky/pre-push new file mode 100755 index 00000000..b9c1cb2f --- /dev/null +++ b/nest-pg-jwt/.husky/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" diff --git a/nest-pg-jwt/.lintstagedrc b/nest-pg-jwt/.lintstagedrc new file mode 100644 index 00000000..22e14328 --- /dev/null +++ b/nest-pg-jwt/.lintstagedrc @@ -0,0 +1,12 @@ +{ + "*.{js,cjs,mjs,ts}": [ + "prettier --write", + "eslint --fix" + ], + "*.md": [ + "prettier --write", + "markdownlint --fix" + ], + "*.sh": "shellcheck -x", + "package.json": "sort-package-json" +} \ No newline at end of file diff --git a/nest-pg-jwt/.ls-lint.yml b/nest-pg-jwt/.ls-lint.yml new file mode 100644 index 00000000..024d598f --- /dev/null +++ b/nest-pg-jwt/.ls-lint.yml @@ -0,0 +1,30 @@ +ls: + # directories + .dir: kebab-case | regex:__tests__ | regex:__mocks__ | regex:__data__ + + # shell scripts + .sh: kebab-case + + # source files + # this is essentially kebab-case.with-custom.extentions.js + .js: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + .ts: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + + # migrations + migrations: + .ts: snake_case + +# ignored directories and files +ignore: + - node_modules + - .git + - .github + - .vscode + - .husky + - dist + - .commitlintrc.js + - .eslintrc.js + - .prettierrc.js + - .openapi + - src/@types + - .devcontainer diff --git a/nest-pg-jwt/.nvmrc b/nest-pg-jwt/.nvmrc new file mode 100644 index 00000000..d3e33db8 --- /dev/null +++ b/nest-pg-jwt/.nvmrc @@ -0,0 +1 @@ +v18.16 diff --git a/nest-pg-jwt/.prettierignore b/nest-pg-jwt/.prettierignore new file mode 100644 index 00000000..b9a7a81d --- /dev/null +++ b/nest-pg-jwt/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +coverage +coverage-e2e +*.config.js +*rc.js diff --git a/nest-pg-jwt/.prettierrc b/nest-pg-jwt/.prettierrc new file mode 100644 index 00000000..dcb72794 --- /dev/null +++ b/nest-pg-jwt/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/nest-pg-jwt/.secrets.baseline b/nest-pg-jwt/.secrets.baseline new file mode 100644 index 00000000..660d13a4 --- /dev/null +++ b/nest-pg-jwt/.secrets.baseline @@ -0,0 +1,116 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-20T21:30:29Z" +} diff --git a/nest-pg-jwt/Dockerfile b/nest-pg-jwt/Dockerfile new file mode 100644 index 00000000..759bf918 --- /dev/null +++ b/nest-pg-jwt/Dockerfile @@ -0,0 +1,89 @@ +# +# 🧑‍💻 Development +# +FROM node:18-alpine AS dev + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Set the working directory +WORKDIR /usr/src/app + +# Set to dev environment +ENV NODE_ENV development + +# Copy the source code +COPY --chown=node:node . . + +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # rebuild bcrypt + && npm rebuild bcrypt + +# +# 🏡 Production Build +# +FROM node:18-alpine as build + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# In order to run `npm run build` we need access to the Nest CLI. +# Nest CLI is a dev dependency. +COPY --chown=node:node --from=dev /usr/src/app/node_modules ./node_modules +# Copy source code +COPY --chown=node:node . . + +# Generate the production build. The build script runs "nest build" to compile the application. +RUN npm run build + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # skip dev dependencies + --omit=dev \ + # rebuild bcrypt + && npm rebuild bcrypt \ + # attempt to reduce image size + && npm cache clean --force + +# Set Docker as a non-root user +USER node + +# +# 🚀 Production Server +# +FROM node:18-alpine as prod + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# Copy only the necessary files +COPY --chown=node:node --from=build /usr/src/app/dist dist +COPY --chown=node:node --from=build /usr/src/app/node_modules node_modules + +# Set Docker as non-root user +USER node + +# Run +CMD [ "node", "./dist/src/main.js" ] \ No newline at end of file diff --git a/nest-pg-jwt/README.md b/nest-pg-jwt/README.md new file mode 100644 index 00000000..c02dae10 --- /dev/null +++ b/nest-pg-jwt/README.md @@ -0,0 +1,260 @@ +# nest-pg-jwt + +## Setup + +### Prerequisites + +#### Node and NPM via NVM + +**Install NVM:** + +Follow the instructions [here](https://github.com/nvm-sh/nvm#installing-and-updating). + +**Install the version of node specified in the `.nvmrc` file and switch to it:** + +```bash +nvm install +nvm use +``` + +**(Optional) Set the node version as default:** + +```bash +nvm alias default $(cat .nvmrc) +``` + +**(Optional) Automatically switch Node version when changing directories:** + +Follow the instructions for your shell [here](https://github.com/nvm-sh/nvm#deeper-shell-integration). + +#### Docker + +Follow the instructions [here](https://docs.docker.com/engine/install/). + +**Post installation steps for Linux users:** + +Follow the instructions [here](https://docs.docker.com/engine/install/linux-postinstall/). + +**Docker Compose:** + +In case `docker compose` isn't setup with the docker installation follow the +instructions [here](https://docs.docker.com/compose/install/). + +### Clone the repo and cd into the folder + +```bash +git clone git@github.com:.git +cd +``` + +### Install Node modules + +```bash +npm install +``` + +### Setup environment variables + +Copy the `.env.example` file to `.env`. Update environment variables as needed. + +```bash +cp .env.example .env +``` + +### Setup environment variables for e2e tests + +Copy the `.env` file to `.env.test`. Update environment variables as needed. + +```bash +cp .env .env.test +``` + +Update `NODE_ENV` to `test`. + +Make sure to provide a different database name from what you have in `.env` +as e2e tests wipe the database clean before they run. + +You may want to disable query logging by removing `DEBUG=knex:query`. + +### Setup docker-compose overrides + +Create a `docker-compose.override.yml` file using the example: + +```bash +cp docker-compose.override.example.yml docker-compose.override.yml +``` + + +Open the `docker-compose.override.yml` file to adjust the database volume path. + +### Provision the services + +In order to provision the services run: + +```bash +docker-compose up -d +``` + + +**Note:** +Make sure to change your database host variable in `.env` and `.env.test` files +to your docker database service name that is in `docker-compose.yml` (e.g. `db`) + +### Dev Containers + +You can work inside a Docker container instead of your host if you like. + +**How to use (VS Code):** + +- Make sure you have installed [Dev Containers Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- Open the project's folder in VS Code +- Hit `Ctrl`/`Cmd` + `Shift` + `P` -> Dev Containers: Open Folder in Container + +For more information on Dev Containers and [supported editors](https://containers.dev/supporting) +you can check the [documentation](https://containers.dev/). + + +## Running the app + +```bash +# build the app +npm run build +# run in development mode +npm run start + +# run in watch mode +npm run start:dev + +# run in debug mode +npm run start:debug +``` + +## Test + +### Unit Tests + +```bash +# run +npm run test + +# watch +npm run test:watch + +# coverage +npm run test:cov +``` + +### End-to-end Tests + +```bash +# e2e tests +npm run test:e2e + +# e2e coverage +npm run test:e2e:cov +``` + +## Working with migrations + +The underlying library is `knex`. You can find their guide on migrations [here](http://knexjs.org/guide/migrations.html). + +```bash +# check what is the last applied migration +npm run db:migrate:version + +# check which migrations have been run and how many are pending +npm run db:migrate:status + +# create a new migration file +npm run db:migrate:make + +# run the next migration that has not yet been run +npm run db:migrate:up + +# undo the last migration that was run +npm run db:migrate:down + +# run all pending migrations +npm run db:migrate:latest + +# rollback the last batch of migrations +npm run db:migrate:rollback + +# rollback all migrations +npm run db:migrate:rollback --all + +# rollback all migrations and re-apply latest +npm run db:migrate:reset +``` + +To debug migrations set the `DEBUG` environment variable to `knex:query`, e.g. + +```bash +DEBUG=knex:query npm run db:migrate:up +``` + +## Working with Docker + +```bash +# build the image +npm run image:build + +# run the image +npm run image:run +``` + + +## Working with OpenAPI + +Swagger Document is being generated when the application is started. +In order to see it you can navigate to `{HOST}:{PORT}/swagger` +e.g. `http://localhost:3000/swagger` (make sure the app is running). + +You can change the route if you want to in `main.ts` + +## Working with Licenses + +Whenever a new `npm` module is added to the project we need to check its +license. If the license is included in the +[OSI approved license list](https://opensource.org/licenses/), then we +can use it. If not - we need to review it manually. Start by running +the following `npm` script: + +```bash +npm run license:check +``` + +If the script succeeds, that means that the license is either a included +in the OSI list, or it was already reviewed and passed the review. When +the script fails it will print the first license that needs a review. +In order to list all licenses that need a review run the following: + +```bash +npm run license:for-review +``` + +In order to verify that a license suits your case, you can use a website like +[SOOS](https://app.soos.io/research/licenses). Find the license and inspect +the `Usage` and `Requirements` sections. You need to verify that the license +is `permissive`. Look for things like whether the license allows for +`Commercial Use` and `Private Use`. + +If the package passes the review, then include it in the list in +`licenses-reviewed.js` located in the root of ths project. + +If the package does not pass the review, then look for an alternative package. + +Lastly, if you just want to print a summary of all license, run: + +```bash +npm run license:summary +``` + +## Debug + +### VS Code + +Go to the Debug menu (CTRL+SHIFT+D). From `RUN AND DEBUG` at the top select +`Run Script: Launch via NPM`. You should now be able to start debugging +by pressing `F5`. + diff --git a/nest-pg-jwt/docker-compose.override.example.yml b/nest-pg-jwt/docker-compose.override.example.yml new file mode 100644 index 00000000..f2ec79b4 --- /dev/null +++ b/nest-pg-jwt/docker-compose.override.example.yml @@ -0,0 +1,6 @@ +services: + + # uncomment the lines below and adjust the volume if desired + # db: + # volumes: + # - /path/to/volume:/var/lib/postgresql/data diff --git a/nest-pg-jwt/docker-compose.yml b/nest-pg-jwt/docker-compose.yml new file mode 100644 index 00000000..a573fc00 --- /dev/null +++ b/nest-pg-jwt/docker-compose.yml @@ -0,0 +1,47 @@ +version: '3' + +services: + db: + image: postgres:13-alpine + container_name: postgres + restart: always + ports: + - '5432:5432' + environment: + POSTGRES_USER: ${PGUSER} + POSTGRES_DB: ${PGDATABASE} + POSTGRES_PASSWORD: ${PGPASSWORD} + volumes: + - db-data:/var/lib/postgresql/data + + api: + build: + context: . + dockerfile: Dockerfile + target: dev + container_name: todo-api + image: todo-api-dev + ports: + - '${PORT}:${PORT}' + env_file: + - .env + # Mount host directory to docker container to support watch mode + volumes: + - .:/usr/src/app + # This ensures that the NestJS container manages the node_modules folder + # rather than synchronizes it with the host machine + - api-node_modules:/usr/src/app/node_modules + depends_on: + - db + command: > + sh -c "cd /usr/src/app + && npm run db:migrate:latest + && npm run start:dev" + +volumes: + api-node_modules: + db-data: + +networks: + default: + name: nest-api diff --git a/nest-pg-jwt/jest.config.js b/nest-pg-jwt/jest.config.js new file mode 100644 index 00000000..575c1728 --- /dev/null +++ b/nest-pg-jwt/jest.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + collectCoverageFrom: ['/src/**/*.(t|j)s'], + coverageDirectory: './coverage', + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/src/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*controller.ts$', + '/src/utils/class-transformers/.*', + '/src/database/seeds/.*', + '/src/.*interceptor.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '.', + testEnvironment: 'node', + testRegex: '.*\\.spec\\.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, +}; diff --git a/nest-pg-jwt/jest.setup.ts b/nest-pg-jwt/jest.setup.ts new file mode 100644 index 00000000..4134a28a --- /dev/null +++ b/nest-pg-jwt/jest.setup.ts @@ -0,0 +1,7 @@ +import '@database/extensions/knex/register'; +import 'reflect-metadata'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/nest-pg-jwt/licenses-allowed.js b/nest-pg-jwt/licenses-allowed.js new file mode 100644 index 00000000..2bee46b1 --- /dev/null +++ b/nest-pg-jwt/licenses-allowed.js @@ -0,0 +1,11 @@ +const spdxLicenses = require('spdx-license-list'); + +const separator = process.argv[2]; + +const osiApprovedLicenses = Object.entries(spdxLicenses) + .filter(([, value]) => value.osiApproved) + .map(([k]) => k); + +const reviewedLicenses = require('./licenses-reviewed'); + +console.log(osiApprovedLicenses.concat(reviewedLicenses).join(separator)); diff --git a/nest-pg-jwt/licenses-reviewed.js b/nest-pg-jwt/licenses-reviewed.js new file mode 100644 index 00000000..e7844022 --- /dev/null +++ b/nest-pg-jwt/licenses-reviewed.js @@ -0,0 +1,34 @@ +module.exports = [ + /** + * @link https://creativecommons.org/licenses/by/1.0/ + * + * Dependencies: + * spdx-license-ids + * spdx-license-list + */ + 'CC0-1.0', + + /** + * @link https://creativecommons.org/licenses/by/3.0/ + * + * Dependencies: + * spdx-ranges + */ + 'CC-BY-3.0', + + /** + * @link https://creativecommons.org/licenses/by/4.0/ + * + * Dependencies: + * caniuse-lite + */ + 'CC-BY-4.0', + + /** + * @link https://blueoakcouncil.org/license/1.0.0 + * + * Dependencies: + * markdownlint-cli + */ + 'BlueOak-1.0.0', +]; diff --git a/nest-pg-jwt/migrations/20230302155244_initial.ts b/nest-pg-jwt/migrations/20230302155244_initial.ts new file mode 100644 index 00000000..e4058ea4 --- /dev/null +++ b/nest-pg-jwt/migrations/20230302155244_initial.ts @@ -0,0 +1,10 @@ +import { Knex } from 'knex'; +import { createUpdateTimestampsFunctionSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.raw(createUpdateTimestampsFunctionSQL); +} + +export async function down(knex: Knex): Promise { + await knex.raw('DROP FUNCTION IF EXISTS update_timestamp() CASCADE;'); +} diff --git a/nest-pg-jwt/migrations/20230302162022_create_table_users.ts b/nest-pg-jwt/migrations/20230302162022_create_table_users.ts new file mode 100644 index 00000000..b3987697 --- /dev/null +++ b/nest-pg-jwt/migrations/20230302162022_create_table_users.ts @@ -0,0 +1,25 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('users', (table) => { + table.bigIncrements('id'); + table + .string('userId') + .unique({ indexName: 'unq_users_user_id' }) + .index(); + table + .string('email') + .notNullable() + .unique({ indexName: 'unq_users_email' }); + table.string('password'); + table.timestamps(false, true, true); + }); + + await knex.raw(createUpdatedAtTriggerSQL('users')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('users')); + await knex.schema.dropTable('users'); +} diff --git a/nest-pg-jwt/migrations/20230302162023_create_table_todos.ts b/nest-pg-jwt/migrations/20230302162023_create_table_todos.ts new file mode 100644 index 00000000..67fedbcb --- /dev/null +++ b/nest-pg-jwt/migrations/20230302162023_create_table_todos.ts @@ -0,0 +1,28 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('todos', (table) => { + table.bigIncrements('id'); + table.string('name').notNullable(); + table.text('note'); + table.boolean('completed').notNullable().defaultTo(false); + table.timestamps(false, true, true); + table.string('userId'); + table + .foreign('userId', 'fk_todos_user_id') + .references('userId') + .inTable('users') + .onDelete('CASCADE'); + table.index('name', 'idx_todos_name'); + table.index('userId', 'idx_todos_userId'); + table.index('createdAt', 'idx_todos_created_at'); + }); + + await knex.raw(createUpdatedAtTriggerSQL('todos')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('todos')); + await knex.schema.dropTable('todos'); +} diff --git a/nest-pg-jwt/migrations/utils/index.ts b/nest-pg-jwt/migrations/utils/index.ts new file mode 100644 index 00000000..bbec689b --- /dev/null +++ b/nest-pg-jwt/migrations/utils/index.ts @@ -0,0 +1,23 @@ +export const createUpdateTimestampsFunctionSQL = ` +CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER +LANGUAGE plpgsql +AS +$$ +BEGIN + NEW."updatedAt" = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$; +`; + +export const createUpdatedAtTriggerSQL = (tableName: string) => ` +CREATE TRIGGER update_timestamp +BEFORE UPDATE +ON ${tableName} +FOR EACH ROW +EXECUTE PROCEDURE update_timestamp(); +`; + +export const dropUpdatedAtTriggerSQL = (tableName: string) => ` +DROP TRIGGER update_timestamp ON ${tableName}; +`; diff --git a/nest-pg-jwt/nest-cli.json b/nest-pg-jwt/nest-cli.json new file mode 100644 index 00000000..f9aa683b --- /dev/null +++ b/nest-pg-jwt/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/nest-pg-jwt/package.json b/nest-pg-jwt/package.json new file mode 100644 index 00000000..04d227c2 --- /dev/null +++ b/nest-pg-jwt/package.json @@ -0,0 +1,107 @@ +{ + "name": "nest-pg-jwt", + "version": "0.0.1", + "private": true, + "description": "", + "license": "UNLICENSED", + "author": "", + "scripts": { + "build": "nest build", + "db:migrate:down": "ts-node node_modules/knex/bin/cli.js migrate:down --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:latest": "ts-node node_modules/knex/bin/cli.js migrate:latest --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:make": "knex migrate:make -x ts --migrations-directory ./migrations", + "db:migrate:reset": "npm run db:migrate:rollback --all && npm run db:migrate:latest", + "db:migrate:rollback": "ts-node node_modules/knex/bin/cli.js migrate:rollback --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:status": "ts-node node_modules/knex/bin/cli.js migrate:status --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:up": "ts-node node_modules/knex/bin/cli.js migrate:up --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "db:migrate:version": "ts-node node_modules/knex/bin/cli.js migrate:currentVersion --migrations-directory ./migrations --client pg --migrations-table-name knex_migrations --connection $(ts-node scripts/db-connection)", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "image:build": "DOCKER_BUILDKIT=1 docker build -t nest-pg-jwt .", + "image:run": "docker run --rm --net host -e NODE_ENV=production --env-file .env nest-pg-jwt", + "license:check": "license-checker --summary --excludePrivatePackages --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --excludePrivatePackages --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "lint:dockerfile": "docker run --rm -i hadolint/hadolint < Dockerfile", + "lint:markdown": "markdownlint **/*.md --ignore node_modules", + "prepare": "husky install", + "start": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start", + "start:debug": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --debug --watch", + "start:dev": "node -r dotenv/config ./node_modules/@nestjs/cli/bin/nest.js start --watch", + "start:prod": "node dist/main", + "test": "jest", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "npx jest --config ./test/jest-e2e.config.js --runInBand", + "test:e2e:cov": "npm run test:e2e -- --coverage", + "test:watch": "jest --watch" + }, + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "express-jwt": "^8.4.1", + "jsonwebtoken": "^9.0.0", + "knex": "^2.4.2", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "18.16.12", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" + } +} \ No newline at end of file diff --git a/nest-pg-jwt/scripts/db-connection.ts b/nest-pg-jwt/scripts/db-connection.ts new file mode 100644 index 00000000..31acfe3c --- /dev/null +++ b/nest-pg-jwt/scripts/db-connection.ts @@ -0,0 +1,3 @@ +console.log( + `postgres://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}` +); diff --git a/nest-pg-jwt/scripts/detect-secrets.sh b/nest-pg-jwt/scripts/detect-secrets.sh new file mode 100755 index 00000000..27d54428 --- /dev/null +++ b/nest-pg-jwt/scripts/detect-secrets.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +RED=$'\e[0;31m' + +echo "[INFO] Running detect-secrets hook" + +# Exit if docker is not found +if ! command -v docker &> /dev/null +then + echo "${RED}[ERROR] docker could not be found! Docker is required in order to run detect-secrets" + exit 1 +fi + +echo "[INFO] If you encounter any issues with detect-secrets, please refer to https://github.com/Yelp/detect-secrets" + +VOLUME_PATH="--volume $(pwd):/usr/src/app" +EXCLUDED_FILES="--exclude-files .*\.(test|spec|e2e)(-e2e|-spec)?\.(js|ts)$ --exclude-files .github/workflows/coverage-e2e.yaml" +STAGED_FILES_PATHS=$(git diff --staged --name-only -z | xargs -0) + +# shellcheck disable=SC2086 +if ! docker run --rm --name detect-secrets $VOLUME_PATH lirantal/detect-secrets $EXCLUDED_FILES --baseline .secrets.baseline $STAGED_FILES_PATHS; +then + echo "${RED}[ERROR] Remove secrets before commiting." + exit 1 +fi + +echo "[INFO] detect-secrets hook completed succesfully. No secrets found!" \ No newline at end of file diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.controller.ts b/nest-pg-jwt/src/api/auth/auth.controller.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/auth.controller.ts rename to nest-pg-jwt/src/api/auth/auth.controller.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.module.ts b/nest-pg-jwt/src/api/auth/auth.module.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/auth.module.ts rename to nest-pg-jwt/src/api/auth/auth.module.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/credentials.dto.ts b/nest-pg-jwt/src/api/auth/dto/credentials.dto.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/dto/credentials.dto.ts rename to nest-pg-jwt/src/api/auth/dto/credentials.dto.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/index.ts b/nest-pg-jwt/src/api/auth/dto/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/dto/index.ts rename to nest-pg-jwt/src/api/auth/dto/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/index.ts b/nest-pg-jwt/src/api/auth/entities/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/entities/index.ts rename to nest-pg-jwt/src/api/auth/entities/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/jwt-token.entity.ts b/nest-pg-jwt/src/api/auth/entities/jwt-token.entity.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/entities/jwt-token.entity.ts rename to nest-pg-jwt/src/api/auth/entities/jwt-token.entity.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.spec.ts b/nest-pg-jwt/src/api/auth/guards/auth.guard.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.spec.ts rename to nest-pg-jwt/src/api/auth/guards/auth.guard.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.ts b/nest-pg-jwt/src/api/auth/guards/auth.guard.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.ts rename to nest-pg-jwt/src/api/auth/guards/auth.guard.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/index.ts b/nest-pg-jwt/src/api/auth/guards/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/guards/index.ts rename to nest-pg-jwt/src/api/auth/guards/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/auth.ts b/nest-pg-jwt/src/api/auth/interfaces/auth.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/auth.ts rename to nest-pg-jwt/src/api/auth/interfaces/auth.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/index.ts b/nest-pg-jwt/src/api/auth/interfaces/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/index.ts rename to nest-pg-jwt/src/api/auth/interfaces/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.spec.ts b/nest-pg-jwt/src/api/auth/services/auth.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.spec.ts rename to nest-pg-jwt/src/api/auth/services/auth.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.ts b/nest-pg-jwt/src/api/auth/services/auth.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.ts rename to nest-pg-jwt/src/api/auth/services/auth.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/index.ts b/nest-pg-jwt/src/api/auth/services/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/index.ts rename to nest-pg-jwt/src/api/auth/services/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.spec.ts b/nest-pg-jwt/src/api/auth/services/jwt.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.spec.ts rename to nest-pg-jwt/src/api/auth/services/jwt.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.ts b/nest-pg-jwt/src/api/auth/services/jwt.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.ts rename to nest-pg-jwt/src/api/auth/services/jwt.service.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.spec.ts b/nest-pg-jwt/src/api/auth/services/password.service.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.spec.ts rename to nest-pg-jwt/src/api/auth/services/password.service.spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.ts b/nest-pg-jwt/src/api/auth/services/password.service.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.ts rename to nest-pg-jwt/src/api/auth/services/password.service.ts diff --git a/nest-pg-jwt/src/api/healthchecks/healthchecks.controller.ts b/nest-pg-jwt/src/api/healthchecks/healthchecks.controller.ts new file mode 100644 index 00000000..d898fd58 --- /dev/null +++ b/nest-pg-jwt/src/api/healthchecks/healthchecks.controller.ts @@ -0,0 +1,50 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '@utils/decorators'; + +@ApiTags('Healthchecks') +@Controller('healthz') +export class HealthchecksController { + @Public() + @Get('live') + @ApiOperation({ + summary: 'Liveness endpoint', + description: 'Used to check whether the application is alive.', + }) + @ApiOkResponse({ + description: 'Application is alive', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + live(): string { + return 'OK'; + } + + @Public() + @Get('ready') + @ApiOperation({ + summary: 'Readiness endpoint', + description: + 'Used to check whether the application is ready to receive requests.', + }) + @ApiOkResponse({ + description: 'Application is ready', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + ready(): string { + return 'OK'; + } +} diff --git a/nest-pg-jwt/src/api/healthchecks/healthchecks.module.ts b/nest-pg-jwt/src/api/healthchecks/healthchecks.module.ts new file mode 100644 index 00000000..797561fc --- /dev/null +++ b/nest-pg-jwt/src/api/healthchecks/healthchecks.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HealthchecksController } from './healthchecks.controller'; + +@Module({ + controllers: [HealthchecksController], +}) +export class HealthchecksModule {} diff --git a/nest-pg-jwt/src/api/todos/__mocks__/index.ts b/nest-pg-jwt/src/api/todos/__mocks__/index.ts new file mode 100644 index 00000000..3f42ef2e --- /dev/null +++ b/nest-pg-jwt/src/api/todos/__mocks__/index.ts @@ -0,0 +1 @@ +export * from './todos.mocks'; diff --git a/nest-pg-jwt/src/api/todos/__mocks__/todos.mocks.ts b/nest-pg-jwt/src/api/todos/__mocks__/todos.mocks.ts new file mode 100644 index 00000000..1ac8f0f0 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/__mocks__/todos.mocks.ts @@ -0,0 +1,73 @@ +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { Paginated } from '@utils/query'; +import { Todo } from '../entities'; +import { TodosSortBy, UpdateTodoDto } from '../dto'; +import { UserData } from '@api/auth/interfaces'; + +export const mockedUser: UserData = { + user: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + email: 'example@email.com', + }, +}; + +const userId = mockedUser.user.sub; + +export const todo: Todo = { + id: 1, + name: 'todo', + note: null, + completed: false, + createdAt: '2023-09-01T09:44:15.515Z', + updatedAt: '2023-09-01T09:44:15.515Z', + userId, +}; + +export const createTodoInput: CreateTodoInput = { + createTodoDto: { + name: todo.name, + completed: todo.completed, + note: todo.note, + }, + userId, +}; + +export const updateTodoDtoInput: UpdateTodoDto = { + name: 'new name', + note: 'new note', + completed: true, +}; + +export const updateTodoInput: UpdateTodoInput = { + id: todo.id, + updateTodoDto: updateTodoDtoInput, + userId, +}; + +export const findOneTodoInput: FindOneTodoInput = { + id: todo.id, + userId, +}; + +export const findAllTodosInput: FindAllTodosInput = { + query: { + pageNumber: 1, + pageSize: 20, + column: TodosSortBy.CreatedAt, + }, + userId, +}; + +export const getPaginatedResponse = (data: T[]): Paginated => { + return { + items: data, + total: data.length, + totalPages: 1, + currentPage: 1, + }; +}; diff --git a/nest-pg-jwt/src/api/todos/dto/create-todo.dto.ts b/nest-pg-jwt/src/api/todos/dto/create-todo.dto.ts new file mode 100644 index 00000000..c100f414 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/dto/create-todo.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Trim } from '@utils/class-transformers'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; + +export class CreateTodoDto { + @ApiProperty() + @Trim() + @MinLength(1) + @MaxLength(255) + name: string; + + @ApiProperty({ type: String, required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/nest-pg-jwt/src/api/todos/dto/find-all-todos-query.dto.ts b/nest-pg-jwt/src/api/todos/dto/find-all-todos-query.dto.ts new file mode 100644 index 00000000..9fe32c7a --- /dev/null +++ b/nest-pg-jwt/src/api/todos/dto/find-all-todos-query.dto.ts @@ -0,0 +1,75 @@ +import { SortOrder } from '@utils/query'; +import { + Allow, + IsEnum, + IsNumber, + IsOptional, + MaxLength, + MinLength, +} from 'class-validator'; +import { Transform } from 'class-transformer'; +import { Trim } from '@utils/class-transformers'; +import { ApiProperty } from '@nestjs/swagger'; + +export enum TodosSortBy { + Name = 'name', + CreatedAt = 'createdAt', +} + +export class FindAllTodosQueryDto { + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ + type: Boolean, + required: false, + }) + @Transform(({ value }) => + value === 'true' ? true : value === 'false' ? false : undefined, + ) + @IsOptional() + completed?: boolean; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @IsEnum(TodosSortBy) + column?: TodosSortBy; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @IsEnum(SortOrder) + @Allow(undefined) + order?: SortOrder | undefined; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number = 1; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number = 20; +} diff --git a/nest-pg-jwt/src/api/todos/dto/index.ts b/nest-pg-jwt/src/api/todos/dto/index.ts new file mode 100644 index 00000000..ea59f55f --- /dev/null +++ b/nest-pg-jwt/src/api/todos/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-todo.dto'; +export * from './find-all-todos-query.dto'; +export * from './update-todo.dto'; diff --git a/nest-pg-jwt/src/api/todos/dto/update-todo.dto.ts b/nest-pg-jwt/src/api/todos/dto/update-todo.dto.ts new file mode 100644 index 00000000..e00f0533 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/dto/update-todo.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateTodoDto } from './create-todo.dto'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; +import { Trim } from '@utils/class-transformers'; + +export class UpdateTodoDto extends PartialType(CreateTodoDto) { + @ApiProperty({ required: false }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/nest-pg-jwt/src/api/todos/entities/index.ts b/nest-pg-jwt/src/api/todos/entities/index.ts new file mode 100644 index 00000000..69b0759f --- /dev/null +++ b/nest-pg-jwt/src/api/todos/entities/index.ts @@ -0,0 +1 @@ +export * from './todo.entity'; diff --git a/nest-pg-jwt/src/api/todos/entities/todo.entity.ts b/nest-pg-jwt/src/api/todos/entities/todo.entity.ts new file mode 100644 index 00000000..2d92e95a --- /dev/null +++ b/nest-pg-jwt/src/api/todos/entities/todo.entity.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { GenericEntity } from '@utils/entities/generic-entity'; + +export class Todo extends GenericEntity { + @ApiProperty() + userId: string; + + @ApiProperty() + name: string; + + @ApiProperty({ nullable: true }) + note: string | null; + + @ApiProperty({ default: false }) + completed = false; +} diff --git a/nest-pg-jwt/src/api/todos/error-mappings/index.ts b/nest-pg-jwt/src/api/todos/error-mappings/index.ts new file mode 100644 index 00000000..cec33482 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './todo-user-not-found.error-mapping'; diff --git a/nest-pg-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/nest-pg-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts new file mode 100644 index 00000000..63ccc517 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts @@ -0,0 +1,6 @@ +import { foreignKeyViolation } from '@database/error-mappings'; + +export const TodoUserNotFound = foreignKeyViolation( + 'fk_todos_user_id', + 'User not found', +); diff --git a/nest-pg-jwt/src/api/todos/interfaces/index.ts b/nest-pg-jwt/src/api/todos/interfaces/index.ts new file mode 100644 index 00000000..1faae003 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/interfaces/index.ts @@ -0,0 +1 @@ +export * from './todos.interface'; diff --git a/nest-pg-jwt/src/api/todos/interfaces/todos.interface.ts b/nest-pg-jwt/src/api/todos/interfaces/todos.interface.ts new file mode 100644 index 00000000..7ce97c70 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/interfaces/todos.interface.ts @@ -0,0 +1,23 @@ +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from '../dto'; + +interface UserData { + userId: string; +} + +interface TodoId { + id?: number; +} + +export interface CreateTodoInput extends UserData { + createTodoDto: CreateTodoDto; +} + +export interface FindAllTodosInput extends UserData { + query: FindAllTodosQueryDto; +} + +export interface FindOneTodoInput extends UserData, TodoId, UpdateTodoDto {} + +export interface UpdateTodoInput extends FindOneTodoInput { + updateTodoDto: UpdateTodoDto; +} diff --git a/nest-pg-jwt/src/api/todos/repositories/index.ts b/nest-pg-jwt/src/api/todos/repositories/index.ts new file mode 100644 index 00000000..2639d438 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/repositories/index.ts @@ -0,0 +1 @@ +export * from './todos.repository'; diff --git a/nest-pg-jwt/src/api/todos/repositories/todos.repository.spec.ts b/nest-pg-jwt/src/api/todos/repositories/todos.repository.spec.ts new file mode 100644 index 00000000..6a5521e3 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/repositories/todos.repository.spec.ts @@ -0,0 +1,142 @@ +import { Test } from '@nestjs/testing'; +import { NestKnexService } from '@database/nest-knex.service'; +import { TodosRepository } from './todos.repository'; +import { + createTodoInput, + findAllTodosInput, + findOneTodoInput, + mockedUser, + todo, + updateTodoInput, +} from '../__mocks__'; + +describe('TodosRepository', () => { + let todosRepository: TodosRepository; + + const first = jest.fn((): Promise => Promise.resolve()); + const del = jest.fn(() => Promise.resolve({})); + const returning = jest.fn().mockImplementation(() => Promise.resolve([])); + const paginate = jest.fn(() => Promise.resolve({})); + + const sort = jest.fn().mockImplementation(() => ({ + paginate, + })); + + const filter = jest.fn().mockImplementation(() => ({ + clone, + })); + + const clone = jest.fn().mockImplementation(() => ({ + sort, + paginate, + })); + + const where = jest.fn().mockImplementation(() => ({ + first, + update, + del, + filter, + })); + + const insert = jest.fn().mockImplementation(() => ({ + returning, + })); + + const update = jest.fn().mockImplementation(() => ({ + returning, + })); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: NestKnexService, + useFactory: () => ({ + connection: () => ({ + insert, + where, + }), + }), + }, + TodosRepository, + ], + }).compile(); + + todosRepository = moduleRef.get(TodosRepository); + }); + + it('create - create a todo', async () => { + returning.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.create(createTodoInput); + + expect(result).toBe(todo); + expect(insert).toHaveBeenCalledWith({ + ...createTodoInput.createTodoDto, + userId: createTodoInput.userId, + }); + }); + + it('findOne - find a todo', async () => { + first.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOne(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(where).toHaveBeenCalledWith(findOneTodoInput); + }); + + it('findOneOrFail - find a todo', async () => { + first.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOneOrFail(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(where).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('update', () => { + it('update - update a todo', async () => { + const updatedTodo = { + ...todo, + name: updateTodoInput.updateTodoDto.name, + }; + + returning.mockImplementationOnce(() => Promise.resolve([updatedTodo])); + + const result = await todosRepository.update(updateTodoInput); + + expect(result).toStrictEqual(updatedTodo); + expect(where).toHaveBeenCalledWith({ + id: updateTodoInput.id, + userId: mockedUser.user.sub, + }); + expect(update).toHaveBeenCalledWith(updateTodoInput.updateTodoDto); + }); + }); + + it('remove - delete a todo', async () => { + del.mockImplementationOnce(() => Promise.resolve(1)); + + const result = await todosRepository.remove(findOneTodoInput); + + expect(result).toBe(1); + expect(where).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('findAll', () => { + it('find all todos for the user', async () => { + paginate.mockImplementationOnce(() => Promise.resolve([todo])); + jest.spyOn(todosRepository, 'count').mockResolvedValueOnce(1); + + const result = await todosRepository.findAll(findAllTodosInput); + + expect(result).toStrictEqual({ + items: [todo], + total: 1, + currentPage: 1, + totalPages: 1, + }); + }); + }); +}); diff --git a/nest-pg-jwt/src/api/todos/repositories/todos.repository.ts b/nest-pg-jwt/src/api/todos/repositories/todos.repository.ts new file mode 100644 index 00000000..bef151ca --- /dev/null +++ b/nest-pg-jwt/src/api/todos/repositories/todos.repository.ts @@ -0,0 +1,92 @@ +import { rethrowError } from '@utils/error'; +import { NestKnexService } from '@database/nest-knex.service'; +import { Paginated } from '@utils/query/pagination'; +import { Todo } from '../entities'; +import { Injectable } from '@nestjs/common'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { definedOrNotFound } from '@utils/query'; +import { TodoUserNotFound } from '../error-mappings'; +import { paginatedResponse } from '@utils/api/response'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; +import { Errors } from '@utils/enums'; + +@Injectable() +export class TodosRepository extends BaseRepository { + constructor(private readonly knex: NestKnexService) { + super(knex, Tables.Todos); + } + + async create(input: CreateTodoInput): Promise { + return this.repository() + .insert({ ...input.createTodoDto, userId: input.userId }) + .returning('*') + .then(([todo]: Todo[]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async findOne(input: Partial): Promise { + return this.repository() + .where({ ...input }) + .first(); + } + + async findOneOrFail(input: Partial): Promise { + return this.repository() + .where({ ...input }) + .first() + .then(definedOrNotFound(Errors.NotFound)); + } + + async update(input: UpdateTodoInput): Promise { + const { id, userId, updateTodoDto } = input; + + return this.repository() + .where({ id, userId }) + .update(updateTodoDto) + .returning('*') + .then(([todo]: Todo[]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async remove(input: FindOneTodoInput): Promise { + return this.repository() + .where({ ...input }) + .del(); + } + + async findAll(input: FindAllTodosInput): Promise> { + const { + userId, + query: { pageNumber, pageSize, name, completed, column, order }, + } = input; + + const pagination = { pageNumber, pageSize }; + const filters = { name, completed }; + + const qb = this.repository().where({ userId }).filter(filters, { + name: this.whereLike, + completed: this.where, + }); + + const itemsQuery = qb.clone(); + + if (column) { + itemsQuery.sort([{ column, order }], { + name: this.orderBy, + createdAt: this.orderBy, + }); + } + + const items = await itemsQuery.paginate(pagination); + + const count = await this.count(qb); + + return paginatedResponse(items, count, pagination); + } +} diff --git a/nest-pg-jwt/src/api/todos/todos.controller.ts b/nest-pg-jwt/src/api/todos/todos.controller.ts new file mode 100644 index 00000000..f1a5f78b --- /dev/null +++ b/nest-pg-jwt/src/api/todos/todos.controller.ts @@ -0,0 +1,106 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Delete, + Req, + ParseIntPipe, + Put, + Query, +} from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { + ApiBadRequestResponse, + ApiBody, + ApiCreatedResponse, + ApiNotFoundResponse, + ApiOkResponse, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { Todo } from './entities'; +import { Paginated } from '@utils/query'; +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from './dto'; +import { UserData } from '@api/auth/interfaces'; +import { + BadRequestDto, + NotFoundDto, + UnprocessableEntityDto, +} from '@utils/dtos'; +import { Errors } from '@utils/enums'; + +@ApiTags('Todos') +@Controller('v1/todos') +export class TodosController { + constructor(private readonly todosService: TodosService) {} + + @ApiBody({ type: CreateTodoDto }) + @ApiCreatedResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @ApiUnprocessableEntityResponse({ + type: UnprocessableEntityDto, + description: Errors.UnprocessableEntity, + }) + @Post() + create( + @Body() createTodoDto: CreateTodoDto, + @Req() { user: { sub } }: UserData, + ): Promise { + return this.todosService.create({ createTodoDto, userId: sub }); + } + + @ApiOkResponse({ type: Todo, isArray: true }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @Get() + findAll( + @Req() { user: { sub } }: UserData, + @Query() query: FindAllTodosQueryDto, + ): Promise> { + return this.todosService.findAll({ userId: sub, query }); + } + + @ApiOkResponse({ type: Todo }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Get(':id') + findOne( + @Param('id', ParseIntPipe) id: number, + @Req() { user: { sub } }: UserData, + ): Promise { + return this.todosService.findOneOrFail({ id, userId: sub }); + } + + @ApiBody({ type: UpdateTodoDto }) + @ApiOkResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Put(':id') + update( + @Param('id', ParseIntPipe) id: number, + @Req() { user: { sub } }: UserData, + @Body() updateTodoDto: UpdateTodoDto, + ): Promise { + return this.todosService.update({ id, userId: sub, updateTodoDto }); + } + + @ApiOkResponse({ type: Number }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Delete(':id') + remove( + @Param('id', ParseIntPipe) id: number, + @Req() { user: { sub } }: UserData, + ): Promise { + return this.todosService.remove({ id, userId: sub }); + } +} diff --git a/nest-pg-jwt/src/api/todos/todos.module.ts b/nest-pg-jwt/src/api/todos/todos.module.ts new file mode 100644 index 00000000..20fefaa8 --- /dev/null +++ b/nest-pg-jwt/src/api/todos/todos.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { DatabaseModule } from '@database/database.module'; +import { TodosRepository } from './repositories/todos.repository'; + +@Module({ + imports: [DatabaseModule], + controllers: [TodosController], + providers: [TodosService, TodosRepository], +}) +export class TodosModule {} diff --git a/nest-pg-jwt/src/api/todos/todos.service.spec.ts b/nest-pg-jwt/src/api/todos/todos.service.spec.ts new file mode 100644 index 00000000..954077de --- /dev/null +++ b/nest-pg-jwt/src/api/todos/todos.service.spec.ts @@ -0,0 +1,163 @@ +import { DatabaseModule } from '@database/database.module'; +import { Test, TestingModule } from '@nestjs/testing'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { TodosRepository } from './repositories'; +import { + createTodoInput, + mockedUser, + getPaginatedResponse, + todo, + updateTodoDtoInput, +} from './__mocks__'; +import { + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { Errors } from '@utils/enums'; +import { dbConfig, nodeConfig } from '@utils/environment'; +import { ConfigModule } from '@nestjs/config'; + +describe('TodosService', () => { + let service: TodosService; + let repository: TodosRepository; + const userId = mockedUser.user.sub; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + DatabaseModule, + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + controllers: [TodosController], + providers: [TodosService, TodosRepository], + }).compile(); + + service = module.get(TodosService); + repository = module.get(TodosRepository); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('create', () => { + it('should return created todo', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => undefined); + jest.spyOn(repository, 'create').mockImplementationOnce(async () => todo); + + expect(await service.create(createTodoInput)).toStrictEqual(todo); + }); + + it('should throw error if todo with the same name exists', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => todo); + + await expect(service.create(createTodoInput)).rejects.toThrowError( + new UnprocessableEntityException(Errors.UnprocessableEntity), + ); + }); + }); + + describe('findAll', () => { + it('should return todos', async () => { + const paginatedResponse = getPaginatedResponse([todo]); + + jest + .spyOn(repository, 'findAll') + .mockImplementationOnce(async () => paginatedResponse); + + expect(await service.findAll({ userId, query: {} })).toStrictEqual( + paginatedResponse, + ); + }); + }); + + describe('findOne', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => todo); + + expect(await service.findOne({ id: todo.id, userId })).toStrictEqual( + todo, + ); + }); + }); + + describe('findOneOrFail', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.findOneOrFail({ id: todo.id, userId }), + ).toStrictEqual(todo); + }); + }); + + describe('update', () => { + it('should update single todo', async () => { + const updatedTodo = { ...todo, ...updateTodoDtoInput }; + + jest + .spyOn(repository, 'update') + .mockImplementationOnce(async () => updatedTodo); + + expect( + await service.update({ + id: todo.id, + userId, + updateTodoDto: updateTodoDtoInput, + }), + ).toStrictEqual(updatedTodo); + }); + + it('should return todo if there is no data to update', async () => { + jest + .spyOn(service, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.update({ + id: todo.id, + userId, + updateTodoDto: {}, + }), + ).toStrictEqual(todo); + }); + }); + + describe('remove', () => { + it('should delete single todo', async () => { + jest.spyOn(service, 'findOne').mockImplementationOnce(async () => todo); + jest.spyOn(repository, 'remove').mockImplementationOnce(async () => 1); + + expect(await service.remove({ id: 1, userId })).toBe(1); + }); + + it('should throw error if todo does not exist', async () => { + jest + .spyOn(service, 'findOne') + .mockImplementationOnce(async () => undefined); + + await expect(service.remove({ id: 1, userId })).rejects.toThrowError( + new NotFoundException(Errors.NotFound), + ); + }); + }); +}); diff --git a/nest-pg-jwt/src/api/todos/todos.service.ts b/nest-pg-jwt/src/api/todos/todos.service.ts new file mode 100644 index 00000000..04651a9d --- /dev/null +++ b/nest-pg-jwt/src/api/todos/todos.service.ts @@ -0,0 +1,70 @@ +import { + Inject, + Injectable, + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { TodosRepository } from './repositories'; +import { Paginated, definedOrNotFound } from '@utils/query'; +import { Todo } from './entities/todo.entity'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from './interfaces'; +import { Errors } from '@utils/enums'; + +@Injectable() +export class TodosService { + constructor( + @Inject(TodosRepository) + private readonly todos: TodosRepository, + ) {} + + async create(input: CreateTodoInput): Promise { + const { + userId, + createTodoDto: { name }, + } = input; + + const todo = await this.todos.findOne({ userId, name }); + + if (todo) { + throw new UnprocessableEntityException(Errors.UnprocessableEntity); + } + + return this.todos.create(input); + } + + findAll(input: FindAllTodosInput): Promise> { + return this.todos.findAll(input); + } + + findOne(input: Partial): Promise { + return this.todos.findOne(input); + } + + findOneOrFail(input: Partial): Promise { + return this.todos.findOneOrFail(input); + } + + update(input: UpdateTodoInput): Promise { + const { id, userId, updateTodoDto } = input; + + if (Object.keys(updateTodoDto).length === 0) { + return this.findOneOrFail({ id, userId }); + } + + return this.todos.update(input).then(definedOrNotFound(Errors.NotFound)); + } + + async remove(input: FindOneTodoInput): Promise { + const todo = await this.findOne(input); + if (!todo) { + throw new NotFoundException(Errors.NotFound); + } + + return this.todos.remove(input); + } +} diff --git a/nest-pg-jwt/src/api/users/entities/index.ts b/nest-pg-jwt/src/api/users/entities/index.ts new file mode 100644 index 00000000..e4aa5074 --- /dev/null +++ b/nest-pg-jwt/src/api/users/entities/index.ts @@ -0,0 +1 @@ +export * from './user.entity'; diff --git a/nest-pg-jwt/src/api/users/entities/user.entity.ts b/nest-pg-jwt/src/api/users/entities/user.entity.ts new file mode 100644 index 00000000..6f0d6bcb --- /dev/null +++ b/nest-pg-jwt/src/api/users/entities/user.entity.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { GenericEntity } from '@utils/entities'; + +export class User extends GenericEntity { + @ApiProperty() + email: string; + + @ApiProperty() + password?: string | null; + + @ApiProperty() + userId: string; +} diff --git a/nest-pg-jwt/src/api/users/error-mappings/index.ts b/nest-pg-jwt/src/api/users/error-mappings/index.ts new file mode 100644 index 00000000..9e6f56e6 --- /dev/null +++ b/nest-pg-jwt/src/api/users/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './user-email-taken.error-mapping'; diff --git a/nest-pg-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/nest-pg-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts new file mode 100644 index 00000000..adfebd97 --- /dev/null +++ b/nest-pg-jwt/src/api/users/error-mappings/user-email-taken.error-mapping.ts @@ -0,0 +1,6 @@ +import { uniqueViolation } from '@database/error-mappings'; + +export const UserEmailTaken = uniqueViolation( + 'unq_users_email', + 'User email already taken', +); diff --git a/nest-pg-jwt/src/api/users/repositories/index.ts b/nest-pg-jwt/src/api/users/repositories/index.ts new file mode 100644 index 00000000..96798590 --- /dev/null +++ b/nest-pg-jwt/src/api/users/repositories/index.ts @@ -0,0 +1 @@ +export * from './users.repository'; diff --git a/nest-pg-jwt/src/api/users/repositories/users.repository.spec.ts b/nest-pg-jwt/src/api/users/repositories/users.repository.spec.ts new file mode 100644 index 00000000..1fe001dd --- /dev/null +++ b/nest-pg-jwt/src/api/users/repositories/users.repository.spec.ts @@ -0,0 +1,107 @@ +import { UsersRepository } from './users.repository'; +import { Test } from '@nestjs/testing'; +import { NestKnexService } from '@database/nest-knex.service'; +import { Credentials } from '@api/auth/interfaces'; + +describe('UsersRepository', () => { + let usersRepository: UsersRepository; + + const first = jest.fn(() => Promise.resolve({})); + const returning = jest.fn().mockImplementation(() => Promise.resolve([])); + + const where = jest.fn().mockImplementation(() => ({ + first, + update, + })); + + const insert = jest.fn().mockImplementation(() => ({ + returning, + })); + + const update = jest.fn().mockImplementation(() => ({ + returning, + })); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: NestKnexService, + useFactory: () => ({ + connection: () => ({ + insert, + where, + update, + }), + }), + }, + UsersRepository, + ], + }).compile(); + + usersRepository = moduleRef.get(UsersRepository); + }); + + it('insertOne - create a user', async () => { + const insertUser: Credentials = { + email: 'user@example.com', + password: 'password', + }; + + const createdUser = { + id: 1, + email: 'user@example.com', + password: 'password', + }; + + returning.mockImplementationOnce(() => Promise.resolve([createdUser])); + + const result = await usersRepository.insertOne({ + email: insertUser.email, + password: insertUser.password, + }); + + expect(result).toBe(createdUser); + expect(insert).toHaveBeenCalledWith({ + email: insertUser.email, + password: insertUser.password, + }); + }); + + it('findByEmail - find a user', async () => { + const userFound = { + id: 1, + email: 'user@example.com', + password: 'password', + }; + + first.mockImplementationOnce(() => Promise.resolve(userFound)); + + const result = await usersRepository.findByEmail('user@example.com'); + + expect(result).toBe(userFound); + expect(where).toHaveBeenCalledWith({ email: 'user@example.com' }); + }); + + it('updateOne - modify a user', async () => { + const updatedUser = { + id: 1, + email: 'user@example.com', + password: 'new-password', + }; + + returning.mockImplementationOnce(() => Promise.resolve([updatedUser])); + + const result = await usersRepository.updateOne(1, { + email: updatedUser.email, + password: updatedUser.password, + }); + + expect(result).toBe(updatedUser); + expect(where).toHaveBeenCalledWith({ id: 1 }); + expect(update).toHaveBeenCalledWith({ + email: updatedUser.email, + password: updatedUser.password, + }); + }); +}); diff --git a/nest-pg-jwt/src/api/users/repositories/users.repository.ts b/nest-pg-jwt/src/api/users/repositories/users.repository.ts new file mode 100644 index 00000000..583af06a --- /dev/null +++ b/nest-pg-jwt/src/api/users/repositories/users.repository.ts @@ -0,0 +1,34 @@ +import { rethrowError } from '@utils/error'; +import { User } from '../entities'; +import { UserEmailTaken } from '../error-mappings'; +import { Injectable } from '@nestjs/common'; +import { NestKnexService } from '@database/nest-knex.service'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; + +@Injectable() +export class UsersRepository extends BaseRepository { + constructor(private readonly knex: NestKnexService) { + super(knex, Tables.Users); + } + + insertOne(payload: Partial): Promise { + return this.repository() + .insert(payload) + .returning('*') + .then((data: any) => data[0]) + .catch(rethrowError(UserEmailTaken)); + } + + findByEmail(email: User['email']): Promise { + return this.repository().where({ email }).first(); + } + + updateOne(id: number, payload: Partial): Promise { + return this.repository() + .where({ id }) + .update(payload) + .returning('*') + .then((data: any) => data[0]) + } +} diff --git a/nest-pg-jwt/src/api/users/users.module.ts b/nest-pg-jwt/src/api/users/users.module.ts new file mode 100644 index 00000000..1ce5ac8b --- /dev/null +++ b/nest-pg-jwt/src/api/users/users.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { UsersRepository } from './repositories'; +import { DatabaseModule } from '@database/database.module'; + +@Module({ + imports: [DatabaseModule], + providers: [UsersRepository], + exports: [UsersRepository], +}) +export class UsersModule {} diff --git a/nest-pg-jwt/src/app.module.ts b/nest-pg-jwt/src/app.module.ts new file mode 100644 index 00000000..6f383b64 --- /dev/null +++ b/nest-pg-jwt/src/app.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DatabaseModule } from '@database/database.module'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; +import { AuthModule } from '@api/auth/auth.module'; +import { UsersModule } from '@api/users/users.module'; +import { TodosModule } from '@api/todos/todos.module'; +import { nodeConfig, dbConfig, authConfig } from '@utils/environment'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + cache: true, + ignoreEnvFile: true, + isGlobal: true, + }), + DatabaseModule, + HealthchecksModule, + UsersModule, + AuthModule, + TodosModule, + ], +}) +export class AppModule {} diff --git a/nest-pg-jwt/src/database/base-repository.repository.spec.ts b/nest-pg-jwt/src/database/base-repository.repository.spec.ts new file mode 100644 index 00000000..c6431bad --- /dev/null +++ b/nest-pg-jwt/src/database/base-repository.repository.spec.ts @@ -0,0 +1,76 @@ +import { Test } from '@nestjs/testing'; +import { NestKnexService } from '@database/nest-knex.service'; +import { BaseRepository } from './base-repository.repository'; +import { Knex } from 'knex'; +import { SortOrder } from '@utils/query'; + +describe('BaseRepository', () => { + let baseRepository: BaseRepository; + + const where = jest.fn((): Promise => Promise.resolve()); + const whereILike = jest.fn((): Promise => Promise.resolve()); + const orderBy = jest.fn((): Promise => Promise.resolve()); + const first = jest.fn((): Promise => Promise.resolve()); + + const count = jest.fn().mockImplementation(() => ({ + first, + })); + + const clone = jest.fn().mockImplementation(() => ({ + count, + })); + + const qb = { + where, + whereILike, + orderBy, + clone, + } as unknown as Knex.QueryBuilder; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: NestKnexService, + useFactory: () => ({ + repository: () => ({ + qb, + }), + }), + }, + BaseRepository, + ], + }).compile(); + + baseRepository = moduleRef.get>(BaseRepository); + }); + + it('where', async () => { + await baseRepository.where(qb, 'testName' as any, 'name'); + + expect(where).toHaveBeenCalled(); + expect(where).toHaveBeenCalledWith({ ['name']: 'testName' }); + }); + + it('whereLike', async () => { + await baseRepository.whereLike(qb, 'testName' as any, 'name'); + + expect(whereILike).toHaveBeenCalled(); + expect(whereILike).toHaveBeenCalledWith('name', `%testName%`); + }); + + it('orderBy', async () => { + await baseRepository.orderBy(qb, SortOrder.Asc, 'name'); + + expect(orderBy).toHaveBeenCalled(); + expect(orderBy).toHaveBeenCalledWith('name', SortOrder.Asc); + }); + + it('count', async () => { + first.mockResolvedValueOnce({ count: 1 }); + + const result = await baseRepository.count(qb); + + expect(result).toBe(1); + }); +}); diff --git a/nest-pg-jwt/src/database/base-repository.repository.ts b/nest-pg-jwt/src/database/base-repository.repository.ts new file mode 100644 index 00000000..e971062a --- /dev/null +++ b/nest-pg-jwt/src/database/base-repository.repository.ts @@ -0,0 +1,38 @@ +import { NestKnexService } from '@database/nest-knex.service'; +import { SortOrder } from '@utils/query'; +import { Knex } from 'knex'; + +// eslint-disable-next-line @typescript-eslint/ban-types +export class BaseRepository { + private knexService; + + constructor(knex: NestKnexService, private readonly tableName: string) { + this.knexService = knex; + } + + repository() { + return this.knexService.connection(this.tableName); + } + + async count(qb: Knex.QueryBuilder): Promise { + const totalCount = await qb.clone().count().first(); + + return +totalCount.count; + } + + where(qb: Knex.QueryBuilder, value: Partial, column: string) { + return qb.where({ [column]: value }); + } + + whereLike( + qb: Knex.QueryBuilder, + value: Partial, + column: string, + ) { + return qb.whereILike(column, `%${value}%`); + } + + orderBy(qb: Knex.QueryBuilder, order: SortOrder, column: string) { + return qb.orderBy(column, order); + } +} diff --git a/nest-pg-jwt/src/database/constants.ts b/nest-pg-jwt/src/database/constants.ts new file mode 100644 index 00000000..cc806173 --- /dev/null +++ b/nest-pg-jwt/src/database/constants.ts @@ -0,0 +1,7 @@ +export const KNEX_CONNECTION = 'KNEX_CONNECTION'; +export const NEST_KNEX_OPTIONS = 'NEST_KNEX_OPTIONS'; + +export enum Tables { + Users = 'users', + Todos = 'todos', +} diff --git a/nest-pg-jwt/src/database/database.module.ts b/nest-pg-jwt/src/database/database.module.ts new file mode 100644 index 00000000..2f93d789 --- /dev/null +++ b/nest-pg-jwt/src/database/database.module.ts @@ -0,0 +1,54 @@ +import { Module, OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import * as pg from 'pg'; +import { dbConfig, nodeConfig } from '@utils/environment'; +import { KNEX_CONNECTION, NEST_KNEX_OPTIONS } from './constants'; +import { NestKnexService } from './nest-knex.service'; + +@Module({ + providers: [ + NestKnexService, + { + provide: NEST_KNEX_OPTIONS, + inject: [dbConfig.KEY, nodeConfig.KEY], + useFactory: ( + database: ConfigType, + node: ConfigType, + ) => ({ + client: 'pg', + useNullAsDefault: true, + connection: { + host: database.PGHOST, + port: database.PGPORT, + user: database.PGUSER, + password: database.PGPASSWORD, + database: database.PGDATABASE, + }, + seeds: { + directory: `./src/database/seeds/${node.NODE_ENV}`, + }, + }), + }, + { + provide: KNEX_CONNECTION, + useFactory: async (nestKnexService: NestKnexService) => + nestKnexService.connection, + inject: [NestKnexService], + }, + ], + exports: [NestKnexService], +}) +export class DatabaseModule implements OnModuleInit, OnApplicationShutdown { + constructor(private readonly knex: NestKnexService) {} + + onModuleInit() { + // https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js + pg.types.setTypeParser(pg.types.builtins.INT8, parseInt); + pg.types.setTypeParser(pg.types.builtins.NUMERIC, parseFloat); + pg.types.setTypeParser(pg.types.builtins.DATE, (v) => v); // keep as string for now + } + + async onApplicationShutdown(): Promise { + await this.knex.connection.destroy(); + } +} diff --git a/nest-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts b/nest-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts new file mode 100644 index 00000000..d71d7805 --- /dev/null +++ b/nest-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts @@ -0,0 +1,46 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { foreignKeyViolation } from './foreign-key-violation.error-mapping'; +import { RecordNotFoundError } from '@database/errors'; + +describe('foreignKeyViolation', () => { + it('should return an error mapping', () => { + expect(foreignKeyViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = foreignKeyViolation( + 'constraint-name', + 'error-message', + ).isError; + + it('should return true when the error is a foreign key constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.FOREIGN_KEY_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a foreign key constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = foreignKeyViolation( + 'constraint-name', + 'error-message', + ).newError; + + it('should return an instance of RecordNotFound with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(RecordNotFoundError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/nest-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/nest-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.ts new file mode 100644 index 00000000..47d378b1 --- /dev/null +++ b/nest-pg-jwt/src/database/error-mappings/foreign-key-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { RecordNotFoundError } from '@database/errors'; +import { isForeignKeyViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const foreignKeyViolation = ( + constraint: string, + message: string, +): ErrorMapping => ({ + isError: isForeignKeyViolation(constraint), + newError: () => new RecordNotFoundError(message), +}); diff --git a/nest-pg-jwt/src/database/error-mappings/index.ts b/nest-pg-jwt/src/database/error-mappings/index.ts new file mode 100644 index 00000000..9f743b83 --- /dev/null +++ b/nest-pg-jwt/src/database/error-mappings/index.ts @@ -0,0 +1,2 @@ +export * from './foreign-key-violation.error-mapping'; +export * from './unique-violation.error-mapping'; diff --git a/nest-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.spec.ts b/nest-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.spec.ts new file mode 100644 index 00000000..6ab85c7d --- /dev/null +++ b/nest-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.spec.ts @@ -0,0 +1,43 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { uniqueViolation } from './unique-violation.error-mapping'; +import { DuplicateRecordError } from '@database/errors'; + +describe('uniqueViolation', () => { + it('should return an error mapping', () => { + expect(uniqueViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = uniqueViolation('constraint-name', 'error-message').isError; + + it('should return true when the error is a unique constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.UNIQUE_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a unique constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = uniqueViolation( + 'constraint-name', + 'error-message', + ).newError; + + it('should return an instance of DuplicateRecord with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(DuplicateRecordError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/nest-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.ts b/nest-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.ts new file mode 100644 index 00000000..0aa2afbf --- /dev/null +++ b/nest-pg-jwt/src/database/error-mappings/unique-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { DuplicateRecordError } from '@database/errors'; +import { isUniqueViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const uniqueViolation = ( + constraint: string, + message: string, +): ErrorMapping => ({ + isError: isUniqueViolation(constraint), + newError: () => new DuplicateRecordError(message), +}); diff --git a/nest-pg-jwt/src/database/errors/duplicate-record.error.ts b/nest-pg-jwt/src/database/errors/duplicate-record.error.ts new file mode 100644 index 00000000..65fbab07 --- /dev/null +++ b/nest-pg-jwt/src/database/errors/duplicate-record.error.ts @@ -0,0 +1,6 @@ +export class DuplicateRecordError extends Error { + constructor(message: string) { + super(message); + this.name = DuplicateRecordError.name; + } +} diff --git a/nest-pg-jwt/src/database/errors/index.ts b/nest-pg-jwt/src/database/errors/index.ts new file mode 100644 index 00000000..7099d226 --- /dev/null +++ b/nest-pg-jwt/src/database/errors/index.ts @@ -0,0 +1,2 @@ +export * from './duplicate-record.error'; +export * from './record-not-found.error'; diff --git a/nest-pg-jwt/src/database/errors/record-not-found.error.ts b/nest-pg-jwt/src/database/errors/record-not-found.error.ts new file mode 100644 index 00000000..52bcb4fa --- /dev/null +++ b/nest-pg-jwt/src/database/errors/record-not-found.error.ts @@ -0,0 +1,6 @@ +export class RecordNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = RecordNotFoundError.name; + } +} diff --git a/nest-pg-jwt/src/database/extensions/knex/index.ts b/nest-pg-jwt/src/database/extensions/knex/index.ts new file mode 100644 index 00000000..825cda2d --- /dev/null +++ b/nest-pg-jwt/src/database/extensions/knex/index.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +import Knex from 'knex'; +import { filter, sort, paginate } from '@database/query-builder'; +import { FilterMap, Pagination, Sort, SorterMap } from '@utils/query'; + +declare module 'knex' { + namespace Knex { + interface QueryBuilder { + /** + * Given a column-to-value map and column-to-filter map, applies the filters to the query builder. + * Filter map must cover all columns in the column-to-value map. + * + * Example: + * ``` + * queryBuilder.filter( + * { + * name: 'John', + * isAncient: true, + * }, + * { + * name: (queryBuilder, name) => queryBuilder.whereILike('name', `%${name}%`), + * isAncient: (queryBuilder, isAncient) => queryBuilder.where('age', isAncient ? '>=' : '<', 100), + * } + * ); + * ``` + */ + filter( + filters: Filters | undefined, + filterMap: FilterMap, + ): this; + /** + * Given a list of column sortings an a column-to-sorter map, applies the sorters to the query builder. + * Sorter map must cover all columns in the column sortings list. + * + * Example: + * ``` + * queryBuilder.sort( + * [ + * { column: 'name' }, + * { column: 'age', order: 'desc'} + * ], + * { + * name: (queryBuilder, order) => queryBuilder.orderBy('name', order), + * age: (queryBuilder, order) => queryBuilder.orderBy('age', order), + * } + * ); + * ``` + */ + sort( + sorts: Sort[] | undefined, + sorterMap: SorterMap, + ): this; + /** + * Offset pagination. This a shorthand for the following: + * + * ``` + * queryBuilder + * .offset((pagination.page - 1) * pagination.items) + * .limit(pagination.items); + * ``` + */ + paginate(pagination?: Pagination): this; + } + } +} + +export const register = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.filter) { + Knex.QueryBuilder.extend('filter', function (filters, filterMap) { + return filters ? filter(this, filters, filterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.sort) { + Knex.QueryBuilder.extend('sort', function (sorts, sorterMap) { + return sorts ? sort(this, sorts || [], sorterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.paginate) { + Knex.QueryBuilder.extend('paginate', function (pagination) { + return paginate(this, pagination); + }); + } +}; diff --git a/nest-pg-jwt/src/database/extensions/knex/register.ts b/nest-pg-jwt/src/database/extensions/knex/register.ts new file mode 100644 index 00000000..07230200 --- /dev/null +++ b/nest-pg-jwt/src/database/extensions/knex/register.ts @@ -0,0 +1,3 @@ +import { register } from './index'; + +register(); diff --git a/nest-pg-jwt/src/database/nest-knex.service.ts b/nest-pg-jwt/src/database/nest-knex.service.ts new file mode 100644 index 00000000..1b21b6ea --- /dev/null +++ b/nest-pg-jwt/src/database/nest-knex.service.ts @@ -0,0 +1,14 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { NEST_KNEX_OPTIONS } from './constants'; +import { knex, Knex } from 'knex'; + +@Injectable() +export class NestKnexService { + public connection: Knex; + + constructor( + @Inject(NEST_KNEX_OPTIONS) private _NestKnexOptions: Knex.Config, + ) { + this.connection = knex(this._NestKnexOptions); + } +} diff --git a/nest-pg-jwt/src/database/query-builder/extensions.spec.ts b/nest-pg-jwt/src/database/query-builder/extensions.spec.ts new file mode 100644 index 00000000..a73f6db1 --- /dev/null +++ b/nest-pg-jwt/src/database/query-builder/extensions.spec.ts @@ -0,0 +1,94 @@ +import { Pagination, Sort, SortOrder } from '@utils/query'; +import { filter, sort, paginate } from './extensions'; + +describe('filter', () => { + it('should apply all the filters to the query builder', () => { + const queryBuilder = {}; + + const filters = { + name: 'John', + height: undefined, + age: 42, + }; + + const filterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _name: string) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _age: number) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + height: jest.fn((qb, _height: number) => qb), + }; + + filter(queryBuilder as never, filters, filterMap as never); + + expect(filterMap.name).toHaveBeenCalledWith( + queryBuilder, + filters.name, + Object.keys(filterMap)[0], + ); + expect(filterMap.height).not.toHaveBeenCalled(); + expect(filterMap.age).toHaveBeenCalledWith( + queryBuilder, + filters.age, + Object.keys(filterMap)[1], + ); + }); +}); + +describe('sort', () => { + it('should apply all the sortings to the query builder', () => { + const queryBuilder = {}; + + const sorts: Sort<'name' | 'age'>[] = [ + { column: 'name' }, + { column: 'age', order: SortOrder.Desc }, + ]; + + const sorterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _order) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _order) => qb), + }; + + sort(queryBuilder as never, sorts, sorterMap); + + expect(sorterMap.name).toHaveBeenCalledWith( + queryBuilder, + SortOrder.Asc, + Object.keys(sorterMap)[0], + ); + expect(sorterMap.age).toHaveBeenCalledWith( + queryBuilder, + SortOrder.Desc, + Object.keys(sorterMap)[1], + ); + }); +}); + +describe('paginate', () => { + it('should apply an offset and a limit to the query builder', () => { + const queryBuilder = { + offset() { + return this; + }, + limit() { + return this; + }, + }; + + jest.spyOn(queryBuilder, 'offset'); + jest.spyOn(queryBuilder, 'limit'); + + const pagination: Pagination = { + pageNumber: 3, + pageSize: 15, + }; + + paginate(queryBuilder as never, pagination); + + expect(queryBuilder.offset).toHaveBeenCalledWith(30); + expect(queryBuilder.limit).toHaveBeenCalledWith(15); + }); +}); diff --git a/nest-pg-jwt/src/database/query-builder/extensions.ts b/nest-pg-jwt/src/database/query-builder/extensions.ts new file mode 100644 index 00000000..00d47324 --- /dev/null +++ b/nest-pg-jwt/src/database/query-builder/extensions.ts @@ -0,0 +1,50 @@ +import { Knex } from 'knex'; +import { + Sort, + Pagination, + FilterMap, + SorterMap, + extractPagination, + SortOrder, +} from '@utils/query'; + +// +// Knex.QueryBuilder extensions +// +export const filter = < + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + Query extends Record, + Filters extends FilterMap>, +>( + qb: Knex.QueryBuilder, + filters: Query, + filterMap: Filters, +): Knex.QueryBuilder => + Object.entries(filters) + .filter(([, v]) => v !== undefined) + .reduce>( + (qb, [k, v]) => filterMap[k as keyof Filters](qb, v as never, k as never), + qb, + ); + +// eslint-disable-next-line @typescript-eslint/ban-types +export const sort = ( + qb: Knex.QueryBuilder, + sorts: Sort[], + sorterMap: SorterMap, +): Knex.QueryBuilder => + sorts.reduce>( + (qb, sort) => + sorterMap[sort.column](qb, sort.order || SortOrder.Asc, sort.column), + qb, + ); + +export const paginate = ( + qb: QB, + pagination?: Pagination, +) => { + const { pageNumber, pageSize } = extractPagination(pagination); + + return qb.offset((pageNumber - 1) * pageSize).limit(pageSize); +}; diff --git a/nest-pg-jwt/src/database/query-builder/index.ts b/nest-pg-jwt/src/database/query-builder/index.ts new file mode 100644 index 00000000..4509f5cc --- /dev/null +++ b/nest-pg-jwt/src/database/query-builder/index.ts @@ -0,0 +1 @@ +export * from './extensions'; diff --git a/nest-pg-jwt/src/database/seeds/test/index.js b/nest-pg-jwt/src/database/seeds/test/index.js new file mode 100644 index 00000000..bab41cb5 --- /dev/null +++ b/nest-pg-jwt/src/database/seeds/test/index.js @@ -0,0 +1,37 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.seed = async function (knex) { + // Deletes ALL existing entries + await knex('users').del(); + await knex('users').insert([ + // The original password for this hash is 'pass@ord' + { + email: 'hello@email.com', + password: '$2b$10$Mxur7NOiTlm22yuldEMZgOCbIV7bxDCcUbBLFbzrJ1MrnIczZB.92', // pragma: allowlist secret + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); + + await knex('todos').insert([ + { + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 2', + note: 'Buy detergent 2', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 3', + note: 'Buy detergent 3', + completed: true, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); +}; diff --git a/nest-pg-jwt/src/database/utils/error.ts b/nest-pg-jwt/src/database/utils/error.ts new file mode 100644 index 00000000..38cf35ea --- /dev/null +++ b/nest-pg-jwt/src/database/utils/error.ts @@ -0,0 +1,17 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; + +export const isDatabaseError = (e: unknown): e is DatabaseError => + e instanceof DatabaseError && + e.code !== undefined && + e.constraint !== undefined; + +export const isUniqueViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.UNIQUE_VIOLATION && + e.constraint === constraint; + +export const isForeignKeyViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.FOREIGN_KEY_VIOLATION && + e.constraint === constraint; diff --git a/nest-pg-jwt/src/database/utils/index.ts b/nest-pg-jwt/src/database/utils/index.ts new file mode 100644 index 00000000..93ae819e --- /dev/null +++ b/nest-pg-jwt/src/database/utils/index.ts @@ -0,0 +1 @@ +export * from './error'; diff --git a/nest-pg-jwt/src/main.ts b/nest-pg-jwt/src/main.ts new file mode 100644 index 00000000..248ec0b5 --- /dev/null +++ b/nest-pg-jwt/src/main.ts @@ -0,0 +1,66 @@ +import '@database/extensions/knex/register'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import helmet from '@fastify/helmet'; +import compression from '@fastify/compress'; +import { HttpAdapterHost, NestFactory } from '@nestjs/core'; +import { ConfigType } from '@nestjs/config'; +import { AppModule } from './app.module'; +import { ValidationPipe } from '@nestjs/common'; +import { RequestLoggingInterceptor } from '@utils/interceptors/request-logging.interceptor'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { ErrorLoggingFilter } from '@utils/error-logging.filter'; +import { nodeConfig } from '@utils/environment'; + +async function bootstrap() { + const app = await NestFactory.create( + AppModule, + new FastifyAdapter(), + ); + + // enables CORS + app.enableCors(); + + // add security HTTP headers + app.register(helmet); + + // compresses response bodies + app.register(compression); + + // enable validation globally + app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); + + // map application level errors to http errors + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + // setup graceful shutdown + app.enableShutdownHooks(); + + const configService = app.get>(nodeConfig.KEY); + const port = configService.PORT; + + if (configService.REQUEST_LOGGING) { + app.useGlobalInterceptors(new RequestLoggingInterceptor()); + } + + if (configService.SWAGGER) { + const config = new DocumentBuilder().setTitle('To-Do Example API').build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('/swagger', app, document); + } + + if (configService.ERROR_LOGGING) { + const httpAdapterHost = app.get(HttpAdapterHost); + app.useGlobalFilters(new ErrorLoggingFilter(httpAdapterHost)); + } + + // start server + await app.listen(port, '0.0.0.0', () => { + console.log(`App is running on http://localhost:${port}`); + }); +} + +bootstrap(); diff --git a/nest-pg-jwt/src/utils/api/index.ts b/nest-pg-jwt/src/utils/api/index.ts new file mode 100644 index 00000000..dbc1ea0f --- /dev/null +++ b/nest-pg-jwt/src/utils/api/index.ts @@ -0,0 +1 @@ +export * from './response'; diff --git a/nest-pg-jwt/src/utils/api/response.ts b/nest-pg-jwt/src/utils/api/response.ts new file mode 100644 index 00000000..89983b94 --- /dev/null +++ b/nest-pg-jwt/src/utils/api/response.ts @@ -0,0 +1,16 @@ +import { Paginated, Pagination, extractPagination } from '@utils/query'; + +export const paginatedResponse = ( + items: T[], + total: number, + pagination?: Pagination, +): Paginated => { + const { pageNumber, pageSize } = extractPagination(pagination); + + return { + items, + total: total, + currentPage: pageNumber, + totalPages: Math.ceil(total / pageSize), + }; +}; diff --git a/nest-pg-jwt/src/utils/class-transformers/index.ts b/nest-pg-jwt/src/utils/class-transformers/index.ts new file mode 100644 index 00000000..fcada5a3 --- /dev/null +++ b/nest-pg-jwt/src/utils/class-transformers/index.ts @@ -0,0 +1,2 @@ +export * from './lower-case'; +export * from './trim'; diff --git a/nest-pg-jwt/src/utils/class-transformers/lower-case.ts b/nest-pg-jwt/src/utils/class-transformers/lower-case.ts new file mode 100644 index 00000000..03897a90 --- /dev/null +++ b/nest-pg-jwt/src/utils/class-transformers/lower-case.ts @@ -0,0 +1,16 @@ +import { Transform } from 'class-transformer'; + +const toLower = (v: string) => v.toLowerCase(); + +export const LowerCase = () => + Transform( + (params) => + Array.isArray(params.value) + ? params.value.map(toLower) + : typeof params.value === 'string' + ? toLower(params.value) + : params.value, + { + toClassOnly: true, + }, + ); diff --git a/nest-pg-jwt/src/utils/class-transformers/trim.ts b/nest-pg-jwt/src/utils/class-transformers/trim.ts new file mode 100644 index 00000000..b261cbce --- /dev/null +++ b/nest-pg-jwt/src/utils/class-transformers/trim.ts @@ -0,0 +1,4 @@ +import { Transform } from 'class-transformer'; + +export const Trim = () => + Transform((params) => params.value?.trim(), { toClassOnly: true }); diff --git a/nest-pg-jwt/src/utils/decorators/index.ts b/nest-pg-jwt/src/utils/decorators/index.ts new file mode 100644 index 00000000..3f75d993 --- /dev/null +++ b/nest-pg-jwt/src/utils/decorators/index.ts @@ -0,0 +1 @@ +export * from './public.decorator'; diff --git a/nest-pg-jwt/src/utils/decorators/public.decorator.spec.ts b/nest-pg-jwt/src/utils/decorators/public.decorator.spec.ts new file mode 100644 index 00000000..b4aab11b --- /dev/null +++ b/nest-pg-jwt/src/utils/decorators/public.decorator.spec.ts @@ -0,0 +1,20 @@ +import { IS_PUBLIC_KEY, Public } from './public.decorator'; + +describe('@Public', () => { + @Public() + class PublicRoutes {} + + class GuardedRoutes {} + + it('should be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, PublicRoutes); + + expect(isPublic).toBe(true); + }); + + it('should not be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, GuardedRoutes); + + expect(isPublic).toBe(undefined); + }); +}); diff --git a/nest-pg-jwt/src/utils/decorators/public.decorator.ts b/nest-pg-jwt/src/utils/decorators/public.decorator.ts new file mode 100644 index 00000000..b3845e12 --- /dev/null +++ b/nest-pg-jwt/src/utils/decorators/public.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/nest-pg-jwt/src/utils/dtos/errors.dto.ts b/nest-pg-jwt/src/utils/dtos/errors.dto.ts new file mode 100644 index 00000000..661bfa22 --- /dev/null +++ b/nest-pg-jwt/src/utils/dtos/errors.dto.ts @@ -0,0 +1,63 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Errors } from '@utils/enums'; + +interface ErrorDto { + statusCode: number; + message: string; + error: string; +} + +export class BadRequestDto implements ErrorDto { + @ApiProperty({ example: 400 }) + statusCode: number; + + @ApiProperty({ example: Errors.BadRequest }) + message: string; + + @ApiProperty({ example: Errors.BadRequest }) + error: string; +} + +export class UnauthorizedDto implements ErrorDto { + @ApiProperty({ example: 401 }) + statusCode: number; + + @ApiProperty({ example: Errors.Unauthorized }) + message: string; + + @ApiProperty({ example: Errors.Unauthorized }) + error: string; +} + +export class NotFoundDto implements ErrorDto { + @ApiProperty({ example: 404 }) + statusCode: number; + + @ApiProperty({ example: 'Record not found' }) + message: string; + + @ApiProperty({ example: Errors.NotFound }) + error: string; +} + +export class ConflictDto implements ErrorDto { + @ApiProperty({ example: 409 }) + statusCode: number; + + @ApiProperty({ example: 'Record already exists' }) + message: string; + + @ApiProperty({ example: Errors.Conflict }) + error: string; +} + +export class UnprocessableEntityDto implements ErrorDto { + @ApiProperty({ example: 422 }) + statusCode: number; + + @ApiProperty({ example: 'Invalid input' }) + message: string; + + @ApiProperty({ example: Errors.UnprocessableEntity }) + error: string; +} diff --git a/nest-pg-jwt/src/utils/dtos/index.ts b/nest-pg-jwt/src/utils/dtos/index.ts new file mode 100644 index 00000000..1a520955 --- /dev/null +++ b/nest-pg-jwt/src/utils/dtos/index.ts @@ -0,0 +1,2 @@ +export * from './pagination.dto'; +export * from './errors.dto'; diff --git a/nest-pg-jwt/src/utils/dtos/pagination.dto.ts b/nest-pg-jwt/src/utils/dtos/pagination.dto.ts new file mode 100644 index 00000000..0bdf43ae --- /dev/null +++ b/nest-pg-jwt/src/utils/dtos/pagination.dto.ts @@ -0,0 +1,14 @@ +import { Transform } from 'class-transformer'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class PaginationDto { + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number; +} diff --git a/nest-pg-jwt/src/utils/entities/generic-entity.ts b/nest-pg-jwt/src/utils/entities/generic-entity.ts new file mode 100644 index 00000000..301c5824 --- /dev/null +++ b/nest-pg-jwt/src/utils/entities/generic-entity.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class GenericEntity { + @ApiProperty() + id: number; + + @ApiProperty() + createdAt: string; + + @ApiProperty() + updatedAt: string; +} diff --git a/nest-pg-jwt/src/utils/entities/index.ts b/nest-pg-jwt/src/utils/entities/index.ts new file mode 100644 index 00000000..1fdd9774 --- /dev/null +++ b/nest-pg-jwt/src/utils/entities/index.ts @@ -0,0 +1 @@ +export * from './generic-entity'; diff --git a/nest-pg-jwt/src/utils/enums/errors.enum.ts b/nest-pg-jwt/src/utils/enums/errors.enum.ts new file mode 100644 index 00000000..8d775147 --- /dev/null +++ b/nest-pg-jwt/src/utils/enums/errors.enum.ts @@ -0,0 +1,7 @@ +export enum Errors { + BadRequest = 'Bad Request', + Unauthorized = 'Unauthorized', + NotFound = 'Not Found', + Conflict = 'Conflict', + UnprocessableEntity = 'Unprocessable Entity', +} diff --git a/nest-pg-jwt/src/utils/enums/index.ts b/nest-pg-jwt/src/utils/enums/index.ts new file mode 100644 index 00000000..efe62e58 --- /dev/null +++ b/nest-pg-jwt/src/utils/enums/index.ts @@ -0,0 +1 @@ +export * from './errors.enum'; diff --git a/assets/nest/multiple-choice-features/environment/pg-jwt/environment.spec.ts b/nest-pg-jwt/src/utils/environment.spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/pg-jwt/environment.spec.ts rename to nest-pg-jwt/src/utils/environment.spec.ts diff --git a/assets/nest/multiple-choice-features/environment/pg-jwt/environment.ts b/nest-pg-jwt/src/utils/environment.ts similarity index 100% rename from assets/nest/multiple-choice-features/environment/pg-jwt/environment.ts rename to nest-pg-jwt/src/utils/environment.ts diff --git a/nest-pg-jwt/src/utils/error-logging.filter.spec.ts b/nest-pg-jwt/src/utils/error-logging.filter.spec.ts new file mode 100644 index 00000000..8b6e8362 --- /dev/null +++ b/nest-pg-jwt/src/utils/error-logging.filter.spec.ts @@ -0,0 +1,68 @@ +import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core'; +import { + ArgumentsHost, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { ErrorLoggingFilter } from './error-logging.filter'; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +jest.spyOn(Logger.prototype, 'error').mockImplementation(() => {}); + +describe('Error logging filter', () => { + let httpAdapterHost: HttpAdapterHost; + let errorLoggingFilter: ErrorLoggingFilter; + const mockStatus = jest.fn(); + const mockGetResponse = jest.fn().mockImplementation(() => ({ + status: mockStatus, + })); + const mockHttpArgumentsHost = jest.fn().mockImplementation(() => ({ + getResponse: mockGetResponse, + })); + const mockArgumentsHost = { + switchToHttp: mockHttpArgumentsHost, + getArgByIndex: jest.fn(), + getArgs: jest.fn(), + getType: jest.fn(), + switchToRpc: jest.fn(), + switchToWs: jest.fn(), + }; + beforeEach(() => { + httpAdapterHost = new HttpAdapterHost(); + httpAdapterHost.httpAdapter = { + reply: jest.fn(), + } as any; + errorLoggingFilter = new ErrorLoggingFilter(httpAdapterHost); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('returns status 500 and logs the error when it is not a generic HTTP exception', () => { + const err = new Error('Generic'); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith(err); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + err, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + }); + + it('returns the status of the HTTP exception and logs the stringified error', () => { + const err = new HttpException('Exception', HttpStatus.BAD_REQUEST); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith('"Exception"'); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + 'Exception', + HttpStatus.BAD_REQUEST, + ); + }); +}); diff --git a/nest-pg-jwt/src/utils/error-logging.filter.ts b/nest-pg-jwt/src/utils/error-logging.filter.ts new file mode 100644 index 00000000..3371caf5 --- /dev/null +++ b/nest-pg-jwt/src/utils/error-logging.filter.ts @@ -0,0 +1,37 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { HttpAdapterHost } from '@nestjs/core'; + +@Catch() +export class ErrorLoggingFilter implements ExceptionFilter { + public logger = new Logger('ErrorLoggingFilter'); + + constructor(private readonly httpAdapterHost: HttpAdapterHost) {} + + public catch(exception: unknown, host: ArgumentsHost): void { + const { httpAdapter } = this.httpAdapterHost; + const ctx = host.switchToHttp(); + + const httpStatus = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + if (exception instanceof HttpException) { + const error = exception.getResponse(); + this.logger.error(JSON.stringify(error)); + + return httpAdapter.reply(ctx.getResponse(), error, httpStatus); + } + + this.logger.error(exception); + + return httpAdapter.reply(ctx.getResponse(), exception, httpStatus); + } +} diff --git a/nest-pg-jwt/src/utils/error.spec.ts b/nest-pg-jwt/src/utils/error.spec.ts new file mode 100644 index 00000000..a0b8baa7 --- /dev/null +++ b/nest-pg-jwt/src/utils/error.spec.ts @@ -0,0 +1,59 @@ +import { ErrorMapping, mapError, rethrowError } from './error'; + +describe('mapError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const newError = new TypeError('type-error'); + + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return newError; + }, + }; + + it('should return the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(mapError(mapping)(error)).toBe(error); + }); + + it('should return the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(mapError(mapping)(error)).toBe(newError); + }); + }); +}); + +describe('rethrowError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return new TypeError('type-error'); + }, + }; + + it('should rethrow the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(() => rethrowError(mapping)(error)).toThrowError(error); + }); + + it('should rethrow the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(() => rethrowError(mapping)(error)).toThrowError( + new TypeError('type-error'), + ); + }); + }); +}); diff --git a/nest-pg-jwt/src/utils/error.ts b/nest-pg-jwt/src/utils/error.ts new file mode 100644 index 00000000..df376a09 --- /dev/null +++ b/nest-pg-jwt/src/utils/error.ts @@ -0,0 +1,28 @@ +type IsErrorPredicate = (error: unknown) => boolean; + +type ErrorFactory = () => Error; + +export interface ErrorMapping { + isError: IsErrorPredicate; + newError: ErrorFactory; +} + +export const mapError = function (...mappings: ErrorMapping[]) { + return function (err: Error) { + for (const { isError, newError } of mappings) { + if (isError(err)) { + return newError(); + } + } + + return err; + }; +}; + +export const rethrowError = function (...mappings: ErrorMapping[]) { + const errorMapper = mapError(...mappings); + + return function (err: Error) { + throw errorMapper(err); + }; +}; diff --git a/nest-pg-jwt/src/utils/interceptors/index.ts b/nest-pg-jwt/src/utils/interceptors/index.ts new file mode 100644 index 00000000..9841f297 --- /dev/null +++ b/nest-pg-jwt/src/utils/interceptors/index.ts @@ -0,0 +1 @@ +export * from './service-to-http-error.interceptor'; diff --git a/nest-pg-jwt/src/utils/interceptors/request-logging.interceptor.ts b/nest-pg-jwt/src/utils/interceptors/request-logging.interceptor.ts new file mode 100644 index 00000000..b8d9197b --- /dev/null +++ b/nest-pg-jwt/src/utils/interceptors/request-logging.interceptor.ts @@ -0,0 +1,83 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, + Logger, +} from '@nestjs/common'; +import { catchError, Observable, tap, throwError } from 'rxjs'; + +const REMOVED = '[[REMOVED]]'; + +@Injectable() +export class RequestLoggingInterceptor implements NestInterceptor { + private logger = new Logger('RequestLoggingInterceptor'); + + private sanitizeHeaders(headers: Record): Record { + const sanitizedHeaders = { ...headers }; + + if (sanitizedHeaders.Authorization) { + sanitizedHeaders.Authorization = REMOVED; + } + + if (sanitizedHeaders.Cookie) { + sanitizedHeaders.Cookie = REMOVED; + } + + return sanitizedHeaders; + } + + private sanitizeBody(body: Record): Record { + const sanitizedBody = { ...body }; + + if (sanitizedBody.password) { + sanitizedBody.password = REMOVED; + } + + return sanitizedBody; + } + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const timestamp = new Date().toISOString(); + const startTime = process.hrtime(); + + const { url, body, method, headers, ip } = context + .switchToHttp() + .getRequest(); + + return next.handle().pipe( + tap(() => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + }; + + this.logger.log(JSON.stringify(logMsg)); + }), + catchError((err: any) => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + err, + }; + + this.logger.log(JSON.stringify(logMsg)); + return throwError(() => err); + }), + ); + } +} diff --git a/nest-pg-jwt/src/utils/interceptors/service-to-http-error.interceptor.ts b/nest-pg-jwt/src/utils/interceptors/service-to-http-error.interceptor.ts new file mode 100644 index 00000000..3a4313fc --- /dev/null +++ b/nest-pg-jwt/src/utils/interceptors/service-to-http-error.interceptor.ts @@ -0,0 +1,31 @@ +import { + CallHandler, + ConflictException, + ExecutionContext, + Injectable, + NestInterceptor, + NotFoundException, +} from '@nestjs/common'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { DuplicateRecordError, RecordNotFoundError } from '@database/errors'; + +const map = { + [RecordNotFoundError.name]: NotFoundException, + [DuplicateRecordError.name]: ConflictException, +}; + +@Injectable() +export class ServiceToHttpErrorsInterceptor implements NestInterceptor { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + catchError((err: Error) => + throwError(() => { + const klass = map[err.name]; + return klass ? new klass(err.message) : err; + }), + ), + ); + } +} diff --git a/nest-pg-jwt/src/utils/query/error.spec.ts b/nest-pg-jwt/src/utils/query/error.spec.ts new file mode 100644 index 00000000..7f02759d --- /dev/null +++ b/nest-pg-jwt/src/utils/query/error.spec.ts @@ -0,0 +1,47 @@ +import { RecordNotFoundError } from '@database/errors'; +import { definedOrNotFound, updatedOrNotFound } from './error'; + +describe('definedOrNotFound', () => { + it('should return a function', () => { + expect(typeof definedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the value it is called with when the value is not undefined', () => { + expect(definedOrNotFound('message')(null)).toBe(null); + expect(definedOrNotFound('message')(false)).toBe(false); + expect(definedOrNotFound('message')(0)).toBe(0); + expect(definedOrNotFound('message')('')).toBe(''); + const emptyArray: unknown[] = []; + expect(definedOrNotFound('message')(emptyArray)).toBe(emptyArray); + const emptyObject = {}; + expect(definedOrNotFound('message')(emptyObject)).toEqual(emptyObject); + }); + + it('should throw RecordNotFound when the value it is called with is undefined', () => { + expect(() => definedOrNotFound('message')(undefined)).toThrowError( + new RecordNotFoundError('message'), + ); + }); + }); +}); + +describe('updatedOrNotFound', () => { + it('should return a function', () => { + expect(typeof updatedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the number it is called with when the number is not zero', () => { + expect(updatedOrNotFound('message')(-1)).toBe(-1); + expect(updatedOrNotFound('message')(1)).toBe(1); + expect(updatedOrNotFound('message')(NaN)).toEqual(NaN); + }); + + it('should throw RecordNotFound when the value it is called with is zero', () => { + expect(() => updatedOrNotFound('message')(0)).toThrowError( + new RecordNotFoundError('message'), + ); + }); + }); +}); diff --git a/nest-pg-jwt/src/utils/query/error.ts b/nest-pg-jwt/src/utils/query/error.ts new file mode 100644 index 00000000..b903287e --- /dev/null +++ b/nest-pg-jwt/src/utils/query/error.ts @@ -0,0 +1,25 @@ +import { RecordNotFoundError } from '@database/errors'; + +const definedOrThrow = (errorFactory: () => Error) => { + return (result: T | undefined): T => { + if (result === undefined) { + throw errorFactory(); + } + return result; + }; +}; + +const updatedOrThrow = (errorFactory: () => Error) => { + return (result: number): number => { + if (result === 0) { + throw errorFactory(); + } + return result; + }; +}; + +export const definedOrNotFound = (message: string) => + definedOrThrow(() => new RecordNotFoundError(message)); + +export const updatedOrNotFound = (message: string) => + updatedOrThrow(() => new RecordNotFoundError(message)); diff --git a/nest-pg-jwt/src/utils/query/filtration.ts b/nest-pg-jwt/src/utils/query/filtration.ts new file mode 100644 index 00000000..b5f151eb --- /dev/null +++ b/nest-pg-jwt/src/utils/query/filtration.ts @@ -0,0 +1,52 @@ +import { Knex } from 'knex'; + +/** + * A Filter is a function that accepts a QueryBuilder for a given entity and a value, + * applies the filter to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type FilterByName = Filter, Todo['name']>; + * + * const filterByName: FilterByName = (qb, name) => qb.where({ name }); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Filter = ( + qb: Knex.QueryBuilder, + value: Value, + key: Key, +) => Knex.QueryBuilder; + +/** + * A FilerMap is a map is a mapping from query param names to filters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all query parameters have a corresponding filter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * // This would typically be obtained via z.infer + * interface ListTodosFilters { + * id?: number; + * name?: string; + * }; + * + * const listTodosFilterMap: FilterMap, ListTodosFilters> = { + * id: (qb, id) => qb.where({ id }), + * name: (qb, name) => qb.whereILike('name', `%${name}%`), + * }; + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type FilterMap = { + // Since query params are usually optional, + // we loop through the query params removing their optionality via `-?`, + // then map them to a filter of their value excluding undefined. + [K in keyof Filters]-?: Filter, undefined>, K>; +}; diff --git a/nest-pg-jwt/src/utils/query/index.ts b/nest-pg-jwt/src/utils/query/index.ts new file mode 100644 index 00000000..3bad38e7 --- /dev/null +++ b/nest-pg-jwt/src/utils/query/index.ts @@ -0,0 +1,4 @@ +export * from './error'; +export * from './filtration'; +export * from './pagination'; +export * from './sorting'; diff --git a/nest-pg-jwt/src/utils/query/pagination.spec.ts b/nest-pg-jwt/src/utils/query/pagination.spec.ts new file mode 100644 index 00000000..378c4a4d --- /dev/null +++ b/nest-pg-jwt/src/utils/query/pagination.spec.ts @@ -0,0 +1,24 @@ +import { + Pagination, + extractPagination, + paginationDefaults, +} from './pagination'; + +describe('extractPagination', () => { + it('should return the provided pagination', () => { + const pagination: Pagination = { pageNumber: 1, pageSize: 10 }; + expect(extractPagination(pagination)).toEqual(pagination); + }); + + it('should supply the defaults for any missing pagination properties', () => { + expect(extractPagination()).toEqual(paginationDefaults); + expect(extractPagination({ pageNumber: 1 })).toEqual({ + ...paginationDefaults, + pageNumber: 1, + }); + expect(extractPagination({ pageSize: 10 })).toEqual({ + ...paginationDefaults, + pageSize: 10, + }); + }); +}); diff --git a/nest-pg-jwt/src/utils/query/pagination.ts b/nest-pg-jwt/src/utils/query/pagination.ts new file mode 100644 index 00000000..a5e5d6e8 --- /dev/null +++ b/nest-pg-jwt/src/utils/query/pagination.ts @@ -0,0 +1,24 @@ +export const paginationDefaults = { + pageNumber: 1, + pageSize: 20, +}; + +export interface Pagination { + pageNumber?: number; + pageSize?: number; +} + +interface PaginationResponse { + totalPages: number; + currentPage: number; +} + +export interface Paginated extends PaginationResponse { + items: Entity[]; + total: number; +} + +export const extractPagination = (pagination?: Pagination) => ({ + ...paginationDefaults, + ...pagination, +}); diff --git a/nest-pg-jwt/src/utils/query/sorting.ts b/nest-pg-jwt/src/utils/query/sorting.ts new file mode 100644 index 00000000..5c9554ef --- /dev/null +++ b/nest-pg-jwt/src/utils/query/sorting.ts @@ -0,0 +1,56 @@ +import { Knex } from 'knex'; + +export enum SortOrder { + Asc = 'asc', + Desc = 'desc', +} + +export interface Sort { + column: SortColumn; + order?: SortOrder; +} + +/** + * A Sorter is a function that accepts a QueryBuilder for a given entity and a sort order, + * applies the sorting to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type SortByName = Sorter>; + * + * const sortByName: SortByName = (qb, order) => qb.orderBy('name', order); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Sorter = ( + qb: Knex.QueryBuilder, + order: SortOrder, + column: SortColumn, +) => Knex.QueryBuilder; + +/** + * A SorterMap is a map is a mapping from query param names to sorters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all sort parameters have a corresponding sorter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type ListTodoSortColumn = 'name' | 'createdAt'; + * + * const listTodosSorterMap: SorterMap, ListTodoSortColumn> = { + * name: (qb, order) => qb.orderBy('name', order), + * createdAt: (qb, order) => qb.orderBy('createdAt', order), + * }; + * ``` + */ +export type SorterMap< + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + SortColumn extends string, +> = Record>; diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/login.e2e-spec.ts b/nest-pg-jwt/test/auth/login.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/login.e2e-spec.ts rename to nest-pg-jwt/test/auth/login.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/register.e2e-spec.ts b/nest-pg-jwt/test/auth/register.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/register.e2e-spec.ts rename to nest-pg-jwt/test/auth/register.e2e-spec.ts diff --git a/nest-pg-jwt/test/healthchecks.e2e-spec.ts b/nest-pg-jwt/test/healthchecks.e2e-spec.ts new file mode 100644 index 00000000..ed67bfa2 --- /dev/null +++ b/nest-pg-jwt/test/healthchecks.e2e-spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; + +describe('GET /healthz', () => { + let app: NestFastifyApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [HealthchecksModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + + it('/live', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/live', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); + + it('/ready', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/ready', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); +}); diff --git a/assets/nest/example-app-pg/test/jest-e2e.config.js b/nest-pg-jwt/test/jest-e2e.config.js similarity index 80% rename from assets/nest/example-app-pg/test/jest-e2e.config.js rename to nest-pg-jwt/test/jest-e2e.config.js index 3714ae0f..2c2968fb 100644 --- a/assets/nest/example-app-pg/test/jest-e2e.config.js +++ b/nest-pg-jwt/test/jest-e2e.config.js @@ -2,21 +2,15 @@ * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - moduleFileExtensions: [ - 'js', - 'json', - 'ts' - ], + moduleFileExtensions: ['js', 'json', 'ts'], rootDir: '..', testEnvironment: 'node', testRegex: '.e2e-spec.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest' + '^.+\\.(t|j)s$': 'ts-jest', }, coverageDirectory: 'coverage-e2e', - collectCoverageFrom: [ - '/src/api/**/*.(t|j)s' - ], + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], coveragePathIgnorePatterns: [ '/src/main.ts$', '/.*spec.ts$', @@ -32,12 +26,10 @@ module.exports = { branches: 85, functions: 85, lines: 85, - statements: 85 - } + statements: 85, + }, }, - setupFiles: [ - '/jest.setup.ts' - ], + setupFiles: ['/jest.setup.ts'], // path aliases from tsconfig.json moduleNameMapper: { '^@api/(.*)$': '/src/api/$1', @@ -47,4 +39,4 @@ module.exports = { '^@middleware$': '/src/middleware', '^@utils/(.*)$': '/src/utils/$1', }, -} +}; diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/create.e2e-spec.ts b/nest-pg-jwt/test/todos/create.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/create.e2e-spec.ts rename to nest-pg-jwt/test/todos/create.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-all.e2e-spec.ts b/nest-pg-jwt/test/todos/find-all.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-all.e2e-spec.ts rename to nest-pg-jwt/test/todos/find-all.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-one.e2e-spec.ts b/nest-pg-jwt/test/todos/find-one.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-one.e2e-spec.ts rename to nest-pg-jwt/test/todos/find-one.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/remove.e2e-spec.ts b/nest-pg-jwt/test/todos/remove.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/remove.e2e-spec.ts rename to nest-pg-jwt/test/todos/remove.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/update.e2e-spec.ts b/nest-pg-jwt/test/todos/update.e2e-spec.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/update.e2e-spec.ts rename to nest-pg-jwt/test/todos/update.e2e-spec.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/get-todo-payload.ts b/nest-pg-jwt/test/todos/utils/get-todo-payload.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/get-todo-payload.ts rename to nest-pg-jwt/test/todos/utils/get-todo-payload.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/index.ts b/nest-pg-jwt/test/todos/utils/index.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/index.ts rename to nest-pg-jwt/test/todos/utils/index.ts diff --git a/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/sortby-field-todos.ts b/nest-pg-jwt/test/todos/utils/sortby-field-todos.ts similarity index 100% rename from assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/sortby-field-todos.ts rename to nest-pg-jwt/test/todos/utils/sortby-field-todos.ts diff --git a/nest-pg-jwt/test/utils/expect-error.ts b/nest-pg-jwt/test/utils/expect-error.ts new file mode 100644 index 00000000..b56c3b27 --- /dev/null +++ b/nest-pg-jwt/test/utils/expect-error.ts @@ -0,0 +1,11 @@ +import { HttpException } from '@nestjs/common'; + +export const expectError = async ( + ex: T, + jsonResponse: Response['json'], +) => { + const response = await jsonResponse(); + + expect(ex.message).toEqual(response.error || response.message); + expect(ex.getStatus()).toEqual(response.statusCode); +}; diff --git a/nest-pg-jwt/test/utils/index.ts b/nest-pg-jwt/test/utils/index.ts new file mode 100644 index 00000000..f788710c --- /dev/null +++ b/nest-pg-jwt/test/utils/index.ts @@ -0,0 +1 @@ +export * from './expect-error'; diff --git a/nest-pg-jwt/tsconfig.build.json b/nest-pg-jwt/tsconfig.build.json new file mode 100644 index 00000000..f54d2265 --- /dev/null +++ b/nest-pg-jwt/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + "test", + "dist", + "**/*spec.ts", + "**/__mocks__" + ] +} diff --git a/nest-pg-jwt/tsconfig.json b/nest-pg-jwt/tsconfig.json new file mode 100644 index 00000000..d8131811 --- /dev/null +++ b/nest-pg-jwt/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "paths": { + "@api/*": ["src/api/*"], + "@database": ["src/database"], + "@database/*": ["src/database/*"], + "@extensions/*": ["src/extensions/*"], + "@middleware": ["src/middleware"], + "@utils/*": ["src/utils/*"] + } + }, + "ts-node": { + "require": ["tsconfig-paths/register", "dotenv/config"] + } +} diff --git a/.commitlintrc.js b/node-cli/.commitlintrc.js similarity index 100% rename from .commitlintrc.js rename to node-cli/.commitlintrc.js diff --git a/node-cli/.editorconfig b/node-cli/.editorconfig new file mode 100644 index 00000000..5e1950da --- /dev/null +++ b/node-cli/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/node-cli/.eslintignore similarity index 100% rename from .eslintignore rename to node-cli/.eslintignore diff --git a/.eslintrc.js b/node-cli/.eslintrc.js similarity index 100% rename from .eslintrc.js rename to node-cli/.eslintrc.js diff --git a/node-cli/.gitignore b/node-cli/.gitignore new file mode 100644 index 00000000..430355d7 --- /dev/null +++ b/node-cli/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +npm-debug.log +coverage +.nyc_output +dist +build +.vscode +.eslintcache +.prettiercache diff --git a/.husky/.gitignore b/node-cli/.husky/.gitignore similarity index 100% rename from .husky/.gitignore rename to node-cli/.husky/.gitignore diff --git a/.husky/commit-msg b/node-cli/.husky/commit-msg similarity index 100% rename from .husky/commit-msg rename to node-cli/.husky/commit-msg diff --git a/.husky/pre-commit b/node-cli/.husky/pre-commit similarity index 100% rename from .husky/pre-commit rename to node-cli/.husky/pre-commit diff --git a/.husky/pre-push b/node-cli/.husky/pre-push similarity index 100% rename from .husky/pre-push rename to node-cli/.husky/pre-push diff --git a/.lintstagedrc.js b/node-cli/.lintstagedrc.js similarity index 100% rename from .lintstagedrc.js rename to node-cli/.lintstagedrc.js diff --git a/.nvmrc b/node-cli/.nvmrc similarity index 100% rename from .nvmrc rename to node-cli/.nvmrc diff --git a/.prettierignore b/node-cli/.prettierignore similarity index 100% rename from .prettierignore rename to node-cli/.prettierignore diff --git a/node-cli/.prettierrc.js b/node-cli/.prettierrc.js new file mode 100644 index 00000000..71eecba5 --- /dev/null +++ b/node-cli/.prettierrc.js @@ -0,0 +1,6 @@ +/** + * @type {import("prettier").Config} + */ +module.exports = { + singleQuote: true, +}; diff --git a/LICENSE b/node-cli/LICENSE similarity index 100% rename from LICENSE rename to node-cli/LICENSE diff --git a/README.md b/node-cli/README.md similarity index 100% rename from README.md rename to node-cli/README.md diff --git a/node-cli/assets/.commitlintrc.js b/node-cli/assets/.commitlintrc.js new file mode 100755 index 00000000..b0f2e40c --- /dev/null +++ b/node-cli/assets/.commitlintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {}, + parserPreset: { + parserOpts: { + referenceActions: null, + issuePrefixes: ['AB#'], + }, + }, +}; diff --git a/node-cli/assets/.czrc b/node-cli/assets/.czrc new file mode 100644 index 00000000..64b8fecd --- /dev/null +++ b/node-cli/assets/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/node-cli/assets/.dockerignore b/node-cli/assets/.dockerignore new file mode 100644 index 00000000..24da266d --- /dev/null +++ b/node-cli/assets/.dockerignore @@ -0,0 +1,15 @@ +.env* +.eslint* +.git +.husky +.nvmrc +.prettier* +.vscode +coverage +coverage-e2e +dist +docker-compose* +Dockerfile +node_modules +README.md +test diff --git a/node-cli/assets/.editorconfig b/node-cli/assets/.editorconfig new file mode 100644 index 00000000..5e1950da --- /dev/null +++ b/node-cli/assets/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/node-cli/assets/.eslintignore b/node-cli/assets/.eslintignore new file mode 100755 index 00000000..0589ba50 --- /dev/null +++ b/node-cli/assets/.eslintignore @@ -0,0 +1,8 @@ +node_modules +.vscode +coverage +dist/ +*.config.js +*rc.js +src/extensions/**/*.js +licenses-allowed.js diff --git a/node-cli/assets/.github/workflows/audit.yaml b/node-cli/assets/.github/workflows/audit.yaml new file mode 100644 index 00000000..d631240e --- /dev/null +++ b/node-cli/assets/.github/workflows/audit.yaml @@ -0,0 +1,16 @@ +name: audit + +on: + pull_request: + branches: + - main + +jobs: + audit: + name: Audit packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run audit + run: npm audit --omit=dev diff --git a/node-cli/assets/.github/workflows/coverage-e2e.yaml b/node-cli/assets/.github/workflows/coverage-e2e.yaml new file mode 100644 index 00000000..599f71aa --- /dev/null +++ b/node-cli/assets/.github/workflows/coverage-e2e.yaml @@ -0,0 +1,42 @@ +name: coverage-e2e + +on: + pull_request: + branches: + - main + +env: + NODE_ENV: test + DEBUG: '' + PORT: 3000 + PGHOST: localhost + PGPORT: 5432 + PGUSER: user + PGPASSWORD: password + PGDATABASE: tests + JWT_SECRET: super-secret + JWT_EXPIRATION: 7200 + +jobs: + coverage-e2e: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Create database + uses: isbang/compose-action@v1.4.1 + - name: Run coverage + uses: ArtiomTr/jest-coverage-report-action@v2 + with: + test-script: npm run test:e2e diff --git a/node-cli/assets/.github/workflows/coverage.yaml b/node-cli/assets/.github/workflows/coverage.yaml new file mode 100644 index 00000000..8de35d91 --- /dev/null +++ b/node-cli/assets/.github/workflows/coverage.yaml @@ -0,0 +1,26 @@ +name: coverage + +on: + pull_request: + branches: + - main + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + contents: write + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Get test coverage + uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/node-cli/assets/.husky/commit-msg b/node-cli/assets/.husky/commit-msg new file mode 100755 index 00000000..9e8dfba1 --- /dev/null +++ b/node-cli/assets/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit $1 diff --git a/node-cli/assets/.husky/gitignorefile b/node-cli/assets/.husky/gitignorefile new file mode 100644 index 00000000..c9cdc63b --- /dev/null +++ b/node-cli/assets/.husky/gitignorefile @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/node-cli/assets/.husky/pre-push b/node-cli/assets/.husky/pre-push new file mode 100755 index 00000000..b9c1cb2f --- /dev/null +++ b/node-cli/assets/.husky/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" diff --git a/assets/.lintstagedrc b/node-cli/assets/.lintstagedrc similarity index 100% rename from assets/.lintstagedrc rename to node-cli/assets/.lintstagedrc diff --git a/assets/.ls-lint.yml b/node-cli/assets/.ls-lint.yml similarity index 100% rename from assets/.ls-lint.yml rename to node-cli/assets/.ls-lint.yml diff --git a/node-cli/assets/.nvmrc b/node-cli/assets/.nvmrc new file mode 100644 index 00000000..d3e33db8 --- /dev/null +++ b/node-cli/assets/.nvmrc @@ -0,0 +1 @@ +v18.16 diff --git a/node-cli/assets/.prettierignore b/node-cli/assets/.prettierignore new file mode 100644 index 00000000..b9a7a81d --- /dev/null +++ b/node-cli/assets/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +coverage +coverage-e2e +*.config.js +*rc.js diff --git a/node-cli/assets/.prettierrc.js b/node-cli/assets/.prettierrc.js new file mode 100644 index 00000000..71eecba5 --- /dev/null +++ b/node-cli/assets/.prettierrc.js @@ -0,0 +1,6 @@ +/** + * @type {import("prettier").Config} + */ +module.exports = { + singleQuote: true, +}; diff --git a/assets/.secrets.baseline b/node-cli/assets/.secrets.baseline similarity index 100% rename from assets/.secrets.baseline rename to node-cli/assets/.secrets.baseline diff --git a/assets/db/mongodb/docker-compose.override.example.yml b/node-cli/assets/db/mongodb/docker-compose.override.example.yml similarity index 100% rename from assets/db/mongodb/docker-compose.override.example.yml rename to node-cli/assets/db/mongodb/docker-compose.override.example.yml diff --git a/assets/db/mongodb/docker-compose.yml b/node-cli/assets/db/mongodb/docker-compose.yml similarity index 100% rename from assets/db/mongodb/docker-compose.yml rename to node-cli/assets/db/mongodb/docker-compose.yml diff --git a/assets/db/pg/docker-compose.override.example.yml b/node-cli/assets/db/pg/docker-compose.override.example.yml similarity index 100% rename from assets/db/pg/docker-compose.override.example.yml rename to node-cli/assets/db/pg/docker-compose.override.example.yml diff --git a/assets/db/pg/docker-compose.yml b/node-cli/assets/db/pg/docker-compose.yml similarity index 100% rename from assets/db/pg/docker-compose.yml rename to node-cli/assets/db/pg/docker-compose.yml diff --git a/node-cli/assets/db/pg/scripts/db-connection.ts b/node-cli/assets/db/pg/scripts/db-connection.ts new file mode 100644 index 00000000..31acfe3c --- /dev/null +++ b/node-cli/assets/db/pg/scripts/db-connection.ts @@ -0,0 +1,3 @@ +console.log( + `postgres://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}` +); diff --git a/node-cli/assets/detect-secrets.sh b/node-cli/assets/detect-secrets.sh new file mode 100755 index 00000000..27d54428 --- /dev/null +++ b/node-cli/assets/detect-secrets.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +RED=$'\e[0;31m' + +echo "[INFO] Running detect-secrets hook" + +# Exit if docker is not found +if ! command -v docker &> /dev/null +then + echo "${RED}[ERROR] docker could not be found! Docker is required in order to run detect-secrets" + exit 1 +fi + +echo "[INFO] If you encounter any issues with detect-secrets, please refer to https://github.com/Yelp/detect-secrets" + +VOLUME_PATH="--volume $(pwd):/usr/src/app" +EXCLUDED_FILES="--exclude-files .*\.(test|spec|e2e)(-e2e|-spec)?\.(js|ts)$ --exclude-files .github/workflows/coverage-e2e.yaml" +STAGED_FILES_PATHS=$(git diff --staged --name-only -z | xargs -0) + +# shellcheck disable=SC2086 +if ! docker run --rm --name detect-secrets $VOLUME_PATH lirantal/detect-secrets $EXCLUDED_FILES --baseline .secrets.baseline $STAGED_FILES_PATHS; +then + echo "${RED}[ERROR] Remove secrets before commiting." + exit 1 +fi + +echo "[INFO] detect-secrets hook completed succesfully. No secrets found!" \ No newline at end of file diff --git a/assets/docker/js/Dockerfile b/node-cli/assets/docker/js/Dockerfile similarity index 100% rename from assets/docker/js/Dockerfile rename to node-cli/assets/docker/js/Dockerfile diff --git a/assets/docker/ts/Dockerfile b/node-cli/assets/docker/ts/Dockerfile similarity index 100% rename from assets/docker/ts/Dockerfile rename to node-cli/assets/docker/ts/Dockerfile diff --git a/assets/express/example-app-auth0/.gitignore b/node-cli/assets/express/example-app-auth0/.gitignore similarity index 100% rename from assets/express/example-app-auth0/.gitignore rename to node-cli/assets/express/example-app-auth0/.gitignore diff --git a/node-cli/assets/express/example-app-auth0/.ls-lint.yml b/node-cli/assets/express/example-app-auth0/.ls-lint.yml new file mode 100644 index 00000000..15fa6973 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/.ls-lint.yml @@ -0,0 +1,29 @@ +ls: + # directories + .dir: kebab-case | regex:__tests__ | regex:__mocks__ | regex:__data__ + + # shell scripts + .sh: kebab-case + + # source files + # this is essentially kebab-case.with-custom.extentions.js + .js: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + .ts: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + + # migrations + migrations: + .ts: snake_case + +# ignored directories and files +ignore: + - node_modules + - .git + - .github + - .vscode + - .husky + - dist + - .commitlintrc.js + - .eslintrc.js + - .prettierrc.js + - .openapi + - src/@types diff --git a/assets/nest/example-app-mongodb/.openapi/gitignorefile b/node-cli/assets/express/example-app-auth0/.openapi/gitignorefile similarity index 100% rename from assets/nest/example-app-mongodb/.openapi/gitignorefile rename to node-cli/assets/express/example-app-auth0/.openapi/gitignorefile diff --git a/node-cli/assets/express/example-app-auth0/Dockerfile b/node-cli/assets/express/example-app-auth0/Dockerfile new file mode 100644 index 00000000..1ba37caf --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/Dockerfile @@ -0,0 +1,79 @@ +# +# Create an intermediate image to build the application that will have devDependencies +# +FROM node:18-alpine AS builder + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Create a directory under the node user +RUN mkdir /app && chown node:node /app + +# Set the working directory +WORKDIR /app + +# Switch to the predefined node use to avoid running as root +USER node + +# Copy both package.json and package-lock.json +COPY --chown=node:node package*.json ./ + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier + +# Copy the source code +COPY --chown=node:node . /app + +# Build the app +RUN npm run build + +# +# This will be the image used in production with just the build and prod dependencies +# +FROM node:18-alpine + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Create a directory under the node user +RUN mkdir -p /usr/src/app && chown node:node /usr/src/app + +# Set the working directory +WORKDIR /usr/src/app + +# Switch to the predefined node use to avoid running as root +USER node + +# Copy both package.json and package-lock.json from the builder image +COPY --from=builder --chown=node:node /app/package*.json ./ + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # skip dev dependencies + --omit=dev \ + # rebuild bcrypt + && npm rebuild bcrypt \ + # attempt to reduce image size + && npm cache clean --force + +# Copy the build from the builder image +COPY --from=builder --chown=node:node /app/dist/ ./dist/ + +# Run +CMD [ "node", "./dist/index.js" ] diff --git a/node-cli/assets/express/example-app-auth0/__mocks__/knex.ts b/node-cli/assets/express/example-app-auth0/__mocks__/knex.ts new file mode 100644 index 00000000..06c77c90 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/__mocks__/knex.ts @@ -0,0 +1,70 @@ +const newQueryBuilder = () => ({ + where() { + return this; + }, + first() { + return this; + }, + update() { + return this; + }, + del() { + return this; + }, + insert() { + return this; + }, + returning() { + return this; + }, + count() { + return this; + }, + clone() { + return this; + }, + filter() { + return this; + }, + sort() { + return this; + }, + paginate() { + return this; + }, + list() { + return this; + }, + then() { + return this; + }, + catch() { + return this; + }, +}); + +const mocks: Record> = {}; + +const knex = jest.fn((name: string) => { + // eslint-disable-next-line security/detect-object-injection + return (mocks[name] ||= newQueryBuilder()); +}); + +interface KnexType { + (): typeof knex; + QueryBuilder: { + extend(): void; + }; +} + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +const Knex: KnexType = jest.fn(() => knex); + +Knex.QueryBuilder = { + extend() { + return; + }, +}; + +export default Knex; diff --git a/node-cli/assets/express/example-app-auth0/docker-compose.override.example.yml b/node-cli/assets/express/example-app-auth0/docker-compose.override.example.yml new file mode 100644 index 00000000..40deb5e0 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/docker-compose.override.example.yml @@ -0,0 +1,12 @@ +services: + + # uncomment the lines below and adjust the volume if desired + # db: + # volumes: + # - /path/to/volume:/var/lib/postgresql/data + + swaggerui: + # uncomment the line below for Apple M1 + # platform: linux/amd64 + ports: + - '3001:8080' diff --git a/node-cli/assets/express/example-app-auth0/docker-compose.yml b/node-cli/assets/express/example-app-auth0/docker-compose.yml new file mode 100644 index 00000000..b2810938 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3' + +services: + + db: + image: postgres:13-alpine + restart: always + ports: + - '${PGPORT}:5432' + environment: + POSTGRES_USER: ${PGUSER} + POSTGRES_DB: ${PGDATABASE} + POSTGRES_PASSWORD: ${PGPASSWORD} + + swaggerui: + image: swaggerapi/swagger-ui + restart: always + ports: + - '3001:8080' + volumes: + - .openapi:/openapi + environment: + SWAGGER_JSON: /openapi/openapi.json + profiles: + - tools diff --git a/node-cli/assets/express/example-app-auth0/jest.config.js b/node-cli/assets/express/example-app-auth0/jest.config.js new file mode 100644 index 00000000..99e9336c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/jest.config.js @@ -0,0 +1,55 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + // run test via Nodejs + testEnvironment: 'node', + // needed for TypeScript + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + // root dir is the app root + rootDir: '.', + // does some setup before each test file + setupFiles: [ + '/jest.setup.ts' + ], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, + // coverage directory + coverageDirectory: '/coverage', + // coverage is collected from files under src/ + collectCoverageFrom: [ + '/src/**/*.[tj]s?(x)' + ], + // and from those files ignore + coveragePathIgnorePatterns: [ + // entry points + '/src/index.[tj]sx?$', + // extensions + '/src/extensions', + // module indexes + '/.*/index.[tj]sx?$', + // e2e tests + '(spec|test)-e2e.[tj]sx?$', + // type definitions + '/src/@types/', + // database migrations + '/src/modules/database/migrations', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, +}; diff --git a/node-cli/assets/express/example-app-auth0/jest.setup.ts b/node-cli/assets/express/example-app-auth0/jest.setup.ts new file mode 100644 index 00000000..c86607a2 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/jest.setup.ts @@ -0,0 +1,2 @@ +import '@extensions/zod/register'; +import '@extensions/knex/register'; \ No newline at end of file diff --git a/node-cli/assets/express/example-app-auth0/migrations/20230302155244_initial.ts b/node-cli/assets/express/example-app-auth0/migrations/20230302155244_initial.ts new file mode 100644 index 00000000..e4058ea4 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/migrations/20230302155244_initial.ts @@ -0,0 +1,10 @@ +import { Knex } from 'knex'; +import { createUpdateTimestampsFunctionSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.raw(createUpdateTimestampsFunctionSQL); +} + +export async function down(knex: Knex): Promise { + await knex.raw('DROP FUNCTION IF EXISTS update_timestamp() CASCADE;'); +} diff --git a/node-cli/assets/express/example-app-auth0/migrations/20230302162022_create_table_users.ts b/node-cli/assets/express/example-app-auth0/migrations/20230302162022_create_table_users.ts new file mode 100644 index 00000000..7119ccb6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/migrations/20230302162022_create_table_users.ts @@ -0,0 +1,25 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('users', (table) => { + table.bigIncrements('id'); + table + .string('userId') + .unique({ indexName: 'unq_users_user_id' }) + .index(); + table + .string('email') + .notNullable() + .unique({ indexName: 'unq_users_email' }); + table.string('password').notNullable(); + table.timestamps(false, true, true); + }); + + await knex.raw(createUpdatedAtTriggerSQL('users')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('users')); + await knex.schema.dropTable('users'); +} diff --git a/node-cli/assets/express/example-app-auth0/migrations/20230302162023_create_table_todos.ts b/node-cli/assets/express/example-app-auth0/migrations/20230302162023_create_table_todos.ts new file mode 100644 index 00000000..930d0c21 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/migrations/20230302162023_create_table_todos.ts @@ -0,0 +1,27 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('todos', (table) => { + table.bigIncrements('id'); + table.string('name').notNullable(); + table.text('note'); + table.boolean('completed').notNullable().defaultTo(false); + table.timestamps(false, true, true); + table.string('userId'); + table + .foreign('userId', 'fk_todos_user_id') + .references('userId') + .inTable('users') + .onDelete('CASCADE'); + table.index('name', 'idx_todos_name'); + table.index('createdAt', 'idx_todos_created_at'); + }); + + await knex.raw(createUpdatedAtTriggerSQL('todos')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('todos')); + await knex.schema.dropTable('todos'); +} diff --git a/node-cli/assets/express/example-app-auth0/migrations/utils/index.ts b/node-cli/assets/express/example-app-auth0/migrations/utils/index.ts new file mode 100644 index 00000000..bbec689b --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/migrations/utils/index.ts @@ -0,0 +1,23 @@ +export const createUpdateTimestampsFunctionSQL = ` +CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER +LANGUAGE plpgsql +AS +$$ +BEGIN + NEW."updatedAt" = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$; +`; + +export const createUpdatedAtTriggerSQL = (tableName: string) => ` +CREATE TRIGGER update_timestamp +BEFORE UPDATE +ON ${tableName} +FOR EACH ROW +EXECUTE PROCEDURE update_timestamp(); +`; + +export const dropUpdatedAtTriggerSQL = (tableName: string) => ` +DROP TRIGGER update_timestamp ON ${tableName}; +`; diff --git a/node-cli/assets/express/example-app-auth0/scripts/generate-openapi.ts b/node-cli/assets/express/example-app-auth0/scripts/generate-openapi.ts new file mode 100644 index 00000000..afd60432 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/scripts/generate-openapi.ts @@ -0,0 +1,42 @@ +// register extensions as the very first thing in the entry point +import '@extensions/zod/register'; + +import { writeFileSync } from 'fs'; +import { resolve } from 'path'; + +import { environmentSchema } from '@utils/environment'; +import { generateDocument } from '@utils/openapi'; +import { healthcheckRoutes } from '@api/healthchecks'; +import { authRoutes } from '@api/auth'; +import { todoRoutes } from '@api/todos'; + +const run = async () => { + const env = Object.freeze(environmentSchema.parse(process.env)); + + const routes = [ + ...healthcheckRoutes, + ...authRoutes, + ...todoRoutes, + ]; + + const document = generateDocument({ + version: '3.0.3', + info: { + version: '1.0.0', + title: 'To-Do', + description: 'A To-Do application API', + }, + routes, + }); + + document.servers = [{ url: `http://localhost:${env.PORT}` }]; + + const file = resolve(__dirname, '..', '.openapi', 'openapi.json'); + const data = JSON.stringify(document, null, 2); + + writeFileSync(file, data); + + return file; +}; + +run().then(console.log).catch(console.error); diff --git a/node-cli/assets/express/example-app-auth0/src/@types/express/index.d.ts b/node-cli/assets/express/example-app-auth0/src/@types/express/index.d.ts new file mode 100644 index 00000000..d74c3318 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/@types/express/index.d.ts @@ -0,0 +1,17 @@ +import { AuthService } from "@api/auth"; +import { TodosService } from "@api/todos"; + +declare module 'express-serve-static-core' { + interface Request { + auth: { + payload: { + sub: string + }, + token: string; + }; + services: { + authService: AuthService; + todosService: TodosService; + }; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/@types/knex/index.d.ts b/node-cli/assets/express/example-app-auth0/src/@types/knex/index.d.ts new file mode 100644 index 00000000..1b277163 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/@types/knex/index.d.ts @@ -0,0 +1,12 @@ +import { Knex } from 'knex'; +import { InsertUser, UpdateUser, User } from '@api/users/entities'; +import { InsertTodo, UpdateTodo, Todo } from '@api/todos/entities'; + +// This is how knex adds TypeScript support. +// For more information see the docs https://knexjs.org/guide/#typescript. +declare module 'knex/types/tables' { + interface Tables { + users: Knex.CompositeTableType; + todos: Knex.CompositeTableType; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/@types/query-types/index.d.ts b/node-cli/assets/express/example-app-auth0/src/@types/query-types/index.d.ts new file mode 100644 index 00000000..6ebc30c8 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/@types/query-types/index.d.ts @@ -0,0 +1,5 @@ +declare module 'query-types' { + import { RequestHandler } from 'express'; + + function middleware(): RequestHandler; +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/guards/auth-guard.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/guards/auth-guard.ts new file mode 100644 index 00000000..bbd067ca --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/guards/auth-guard.ts @@ -0,0 +1,7 @@ +import { auth } from 'express-oauth2-jwt-bearer'; + +export const authGuard = (audience: string, issuerBaseURL: string) => + auth({ + audience: [audience], + issuerBaseURL, + }); diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/guards/index.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/guards/index.ts new file mode 100644 index 00000000..e6aa3e2c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/guards/index.ts @@ -0,0 +1 @@ +export * from './auth-guard'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/index.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/index.ts new file mode 100644 index 00000000..6c432b8b --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/index.ts @@ -0,0 +1,3 @@ +export * from './guards'; +export * from './services'; +export * from './routes'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/interfaces/auth.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/interfaces/auth.ts new file mode 100644 index 00000000..1123a9de --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/interfaces/auth.ts @@ -0,0 +1,22 @@ +export interface Credentials { + email: string; + password: string; +} + +export type Auth0User = { + created_at: string; + email: string; + email_verified: boolean; + identities: { + connection: string; + user_id: string; + provider: string; + isSocial: boolean; + }[]; + name: string; + nickname: string; + picture: string; + updated_at: string; + user_id: string; + user_metadata: Record; +}; \ No newline at end of file diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/interfaces/index.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/interfaces/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/interfaces/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/routes/index.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/routes/index.ts new file mode 100644 index 00000000..2421a50f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/routes/index.ts @@ -0,0 +1,4 @@ +import { prefixRoutes } from '@utils/api'; +import { registerRoute } from './register.route'; + +export const authRoutes = prefixRoutes('/auth', [registerRoute]); diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/routes/register.route.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/routes/register.route.ts new file mode 100644 index 00000000..a7ff58d6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/routes/register.route.ts @@ -0,0 +1,22 @@ +import { asyncHandler, defineRoute } from '@utils/api'; +import { UserSchema, credentialsSchema } from '../schemas'; + +export const registerRoute = defineRoute({ + operationId: 'register', + summary: 'Register a user', + description: 'Register a user', + tags: ['Auth'], + method: 'post', + path: '/register', + request: { + body: credentialsSchema, + }, + responses: { + 200: UserSchema + }, +}).attachHandler( + asyncHandler(async ({ body, services }, res) => { + const user = await services.authService.register(body); + res.status(200).send(user); + }) +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/credentials.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/credentials.schema.ts new file mode 100644 index 00000000..d3581270 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/credentials.schema.ts @@ -0,0 +1,9 @@ +import { z } from 'zod'; +import { email, password } from '@utils/validation'; + +export const credentialsSchema = z + .object({ + email: email, + password: password, + }) + .openapi({ ref: 'Credentials' }); diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/index.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/index.ts new file mode 100644 index 00000000..f74e70bf --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/index.ts @@ -0,0 +1,2 @@ +export * from './credentials.schema'; +export * from './user.schema'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/user.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/user.schema.ts new file mode 100644 index 00000000..903a69b5 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/schemas/user.schema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +export const UserSchema = z + .object({ + userId: z.string(), + email: z.string(), + }) + .openapi({ ref: 'User' }); diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/services/auth.service.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/services/auth.service.ts new file mode 100644 index 00000000..7ccaf3d3 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/services/auth.service.ts @@ -0,0 +1,42 @@ +import { UsersRepository } from '@api/users'; +import { Logger } from 'pino'; +import { Auth0Service } from './auth0.service'; +import createHttpError from 'http-errors'; +import { Credentials } from '../interfaces'; + +export class AuthService { + constructor( + private logger: Logger, + private auth0Service: Auth0Service, + private usersRepository: UsersRepository, + ) {} + + async register({ email, password }: Credentials) { + const userAuth0 = await this.auth0Service.createUser(email, password); + + const user = await this.usersRepository + .insertOne({ + email, + userId: userAuth0.user_id, + }) + .catch(async (error) => { + this.logger.warn( + 'Creating a user in the database failed. Proceeding with deleting it in Auth0.' + ); + this.logger.error(error); + + await this.auth0Service.deleteUser(userAuth0.user_id); + + throw new createHttpError.BadRequest('Something went wrong!'); + }); + + await this.auth0Service.updateUserMetadata(userAuth0.user_id, { + id: user.id, + }); + + return { + email: user.email, + userId: user.userId, + }; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/services/auth0.service.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/services/auth0.service.ts new file mode 100644 index 00000000..2efb658d --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/services/auth0.service.ts @@ -0,0 +1,127 @@ +import { AxiosStatic } from 'axios'; +import { Logger } from 'pino'; +import { Auth0User } from '../interfaces'; +import { Environment } from '@utils/environment'; +import createHttpError from 'http-errors'; + +export class Auth0Service { + private accessToken = ''; + private baseURL = this.env.AUTH0_ISSUER_URL; + private AUTH0_CLIENT_ID = this.env.AUTH0_CLIENT_ID; + private AUTH0_CLIENT_SECRET = this.env.AUTH0_CLIENT_SECRET; + + constructor( + private logger: Logger, + private axios: AxiosStatic, + private env: Environment, + ) { + this.getAuth0AccessToken() + .then((token) => { + this.accessToken = token; + }) + .catch((err) => { + throw err; + }); + } + + private async getAuth0AccessToken() { + const response = await this.axios + .post<{ access_token: string }>( + `${this.baseURL}oauth/token`, + new URLSearchParams({ + grant_type: 'client_credentials', + client_id: this.AUTH0_CLIENT_ID, + client_secret: this.AUTH0_CLIENT_SECRET, + audience: `${this.baseURL}api/v2/`, + }), + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + } + ) + .catch((error) => { + this.logger.error(error.response.data); + throw new createHttpError.BadRequest('Something went wrong!'); + }); + + if (!response?.data?.access_token) { + this.logger.error('Access token is missing in the response data'); + throw new createHttpError.BadRequest('Access token is missing!'); + } + + return response.data.access_token; + } + + private buildHeaders() { + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: `Bearer ${this.accessToken}`, + }; + } + + public async createUser(email: string, password: string) { + const users = await this.searchUsersByEmail(email); + if (Array.isArray(users) && users.length) { + throw new createHttpError.BadRequest( + 'A user with this email already exists.' + ); + } + + return this.axios + .post( + `${this.baseURL}api/v2/users`, + { + email, + user_metadata: {}, + connection: 'Username-Password-Authentication', + password, + verify_email: true, + }, + { headers: this.buildHeaders() } + ) + .then(({ data }) => data) + .catch((error) => { + this.logger.error(error.response.data); + throw new createHttpError.BadRequest('Something went wrong!'); + }); + } + + public updateUserMetadata( + userId: string, + metadata: Auth0User['user_metadata'] + ) { + return this.axios.patch( + `${this.baseURL}api/v2/users/${userId}`, + { + user_metadata: metadata, + }, + { headers: this.buildHeaders() } + ); + } + + public searchUsersByEmail(email: string) { + return this.axios + .get(`${this.baseURL}api/v2/users-by-email`, { + params: { email }, + headers: this.buildHeaders(), + }) + .then(({ data }) => data) + .catch((error) => { + this.logger.error(error.response.data); + throw new createHttpError.BadRequest('Something went wrong!'); + }); + } + + public deleteUser(userId: string) { + return this.axios + .delete(`${this.baseURL}api/v2/users/${userId}`, { + headers: this.buildHeaders(), + }) + .catch((error) => { + this.logger.error(error.response.data); + throw new createHttpError.BadRequest('Something went wrong!'); + }); + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/auth/services/index.ts b/node-cli/assets/express/example-app-auth0/src/api/auth/services/index.ts new file mode 100644 index 00000000..aca3fc56 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/auth/services/index.ts @@ -0,0 +1,2 @@ +export * from './auth.service'; +export * from './auth0.service'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/healthchecks/index.ts b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/index.ts new file mode 100644 index 00000000..a3820983 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/index.ts @@ -0,0 +1 @@ +export * from './routes'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/index.ts b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/index.ts new file mode 100644 index 00000000..063c5cb1 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/index.ts @@ -0,0 +1,8 @@ +import { prefixRoutes } from '@utils/api'; +import { livenessRoute } from './live.route'; +import { readinessRoute } from './ready.route'; + +export const healthcheckRoutes = prefixRoutes('/healthz', [ + livenessRoute, + readinessRoute, +]); diff --git a/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.test.ts new file mode 100644 index 00000000..1f66d086 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.test.ts @@ -0,0 +1,15 @@ +import { livenessRoute as route } from './live.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + it('should respond with "OK"', () => { + const res = { send: jest.fn() }; + route.handler({} as never, res as never, jest.fn()); + expect(res.send).toHaveBeenCalledWith('OK'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.ts b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.ts new file mode 100644 index 00000000..d20d6104 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/live.route.ts @@ -0,0 +1,26 @@ +import { RouteDefinition } from '@utils/api'; + +export const livenessRoute: RouteDefinition = { + operationId: 'health-liveness', + summary: 'Liveness endpoint', + description: 'Used to check whether the application is alive.', + tags: ['Healthchecks'], + method: 'get', + path: '/live', + handler: function (_req, res) { + res.send('OK'); + }, + responses: { + 200: { + description: 'Application is alive', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }, + }, +}; diff --git a/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.test.ts new file mode 100644 index 00000000..4a751b8f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.test.ts @@ -0,0 +1,15 @@ +import { readinessRoute as route } from './ready.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + it('should respond with "OK"', () => { + const res = { send: jest.fn() }; + route.handler({} as never, res as never, jest.fn()); + expect(res.send).toHaveBeenCalledWith('OK'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.ts b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.ts new file mode 100644 index 00000000..a6d4ef7c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/healthchecks/routes/ready.route.ts @@ -0,0 +1,28 @@ +import { RouteDefinition } from '@utils/api'; + +export const readinessRoute: RouteDefinition = { + operationId: 'health-readiness', + summary: 'Readiness endpoint', + description: + 'Used to check whether the application is ready to receive requests.', + tags: ['Healthchecks'], + method: 'get', + path: '/ready', + handler: function (_req, res) { + // Add your specific checks + res.send('OK'); + }, + responses: { + 200: { + description: 'Application is ready', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }, + }, +}; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/entities/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/index.ts new file mode 100644 index 00000000..1382a966 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/index.ts @@ -0,0 +1,4 @@ +export * from './insert-todo.entity'; +export * from './list-todos-query.entity'; +export * from './todo.entity'; +export * from './update-todo.entity'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/entities/insert-todo.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/insert-todo.entity.ts new file mode 100644 index 00000000..d7c00eca --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/insert-todo.entity.ts @@ -0,0 +1,6 @@ +import { Insert } from '@database/operations'; +import { Todo } from './todo.entity'; + +export type InsertTodo = Insert< + Pick +>; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/entities/list-todos-query.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/list-todos-query.entity.ts new file mode 100644 index 00000000..f1285315 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/list-todos-query.entity.ts @@ -0,0 +1,13 @@ +import { Pagination } from '@utils/query'; +import { Todo } from './todo.entity'; + +export interface ListTodosQuery { + filters?: Partial>; + sorts?: + | { + column: 'name' | 'createdAt'; + order?: 'asc' | 'desc' | undefined; + }[] + | undefined; + pagination?: Pagination; +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/entities/todo.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/todo.entity.ts new file mode 100644 index 00000000..c8fce6bc --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/todo.entity.ts @@ -0,0 +1,9 @@ +export interface Todo { + id: number; + userId: string; + name: string; + note: string | null; + completed: boolean; + createdAt: string; + updatedAt: string; +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/entities/update-todo.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/update-todo.entity.ts new file mode 100644 index 00000000..b94d8a94 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/entities/update-todo.entity.ts @@ -0,0 +1,4 @@ +import { Update } from '@database/operations'; +import { Todo } from './todo.entity'; + +export type UpdateTodo = Update>; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/error-mappings/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/error-mappings/index.ts new file mode 100644 index 00000000..cec33482 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './todo-user-not-found.error-mapping'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts new file mode 100644 index 00000000..02f8ead3 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts @@ -0,0 +1,6 @@ +import { foreignKeyViolation } from '@database/error-mappings'; + +export const TodoUserNotFound = foreignKeyViolation( + 'fk_todos_user_id', + 'User not found' +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/filters/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/index.ts new file mode 100644 index 00000000..25a6d221 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/index.ts @@ -0,0 +1,2 @@ +export * from './todos-by-completed.filter'; +export * from './todos-by-name.filter'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts new file mode 100644 index 00000000..1706a438 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.test.ts @@ -0,0 +1,15 @@ +import { filterByCompleted } from './todos-by-completed.filter'; + +const queryBuilder = { + whereILike: jest.fn(), + where: jest.fn(), +}; + +describe('filterByCompleted', () => { + const completed = true; + + it('should filter by completed', () => { + filterByCompleted(queryBuilder as never, completed); + expect(queryBuilder.where).toHaveBeenCalledWith({ completed }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.ts new file mode 100644 index 00000000..29f9eddc --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-completed.filter.ts @@ -0,0 +1,7 @@ +import { Tables } from 'knex/types/tables'; +import { Filter } from '@utils/query'; + +export const filterByCompleted: Filter = ( + qb, + completed +) => qb.where({ completed }); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.test.ts new file mode 100644 index 00000000..af71252e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.test.ts @@ -0,0 +1,15 @@ +import { filterByName } from './todos-by-name.filter'; + +const queryBuilder = { + whereILike: jest.fn(), + where: jest.fn(), +}; + +describe('filterByName', () => { + const name = 'Jonh'; + + it('should filter by name', () => { + filterByName(queryBuilder as never, name); + expect(queryBuilder.whereILike).toHaveBeenCalledWith('name', `%${name}%`); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.ts new file mode 100644 index 00000000..be32c21c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/filters/todos-by-name.filter.ts @@ -0,0 +1,5 @@ +import { Tables } from 'knex/types/tables'; +import { Filter } from '@utils/query'; + +export const filterByName: Filter = (qb, name) => + qb.whereILike('name', `%${name}%`); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/index.ts new file mode 100644 index 00000000..5833c69d --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/index.ts @@ -0,0 +1,8 @@ +export * from './entities'; +export * from './error-mappings'; +export * from './filters'; +export * from './repositories'; +export * from './routes'; +export * from './schemas'; +export * from './services'; +export * from './sorters'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts new file mode 100644 index 00000000..23f212d5 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/__mocks__/todos.repository.ts @@ -0,0 +1,82 @@ +import { Paginated, extractPagination } from '@utils/query'; +import { InsertTodo, ListTodosQuery, Todo, UpdateTodo } from '../../entities'; + +export class TodosRepository { + private lastId = 0; + private records: Todo[] = []; + + private nextId() { + return ++this.lastId; + } + + async insertOne(input: InsertTodo): Promise { + const record = { + id: this.nextId(), + ...input, + note: input.note ?? null, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + this.records.push(record); + + return record; + } + + async findById( + id: Todo['id'], + userId: Todo['userId'] + ): Promise { + return this.records.find((r) => r.id === id && r.userId === userId); + } + + async updateById( + id: Todo['id'], + userId: Todo['userId'], + input: UpdateTodo + ): Promise { + const record = await this.findById(id, userId); + + if (!record) { + return; + } + + Object.assign(record, input); + + return record; + } + + async deleteById(id: Todo['id'], userId: Todo['userId']): Promise { + const record = await this.findById(id, userId); + + if (!record) { + return 0; + } + + this.records.splice(this.records.indexOf(record), 1); + + return 1; + } + + async list( + userId: Todo['userId'], + query: ListTodosQuery + ): Promise> { + const { page, items } = extractPagination(query.pagination); + + const records = this.records.filter((r) => r.userId === userId); + + const start = (page - 1) * items; + const end = start + items; + const data = records.slice(start, end); + + return { + data, + meta: { + total: records.length, + page, + items, + }, + }; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/index.ts new file mode 100644 index 00000000..2639d438 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/index.ts @@ -0,0 +1 @@ +export * from './todos.repository'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.test.ts new file mode 100644 index 00000000..bdcd924c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.test.ts @@ -0,0 +1,198 @@ +import Knex from 'knex'; +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { RecordNotFoundError } from '@database/errors'; +import { Todo } from '../entities'; +import { TodosRepository } from './todos.repository'; +import { filterByCompleted, filterByName } from '../filters'; +import { sortByCreatedAt, sortByName } from '../sorters'; + +describe('TodosRepository', () => { + const knex = Knex({ client: 'pg', connection: {} }); + const todos = new TodosRepository(knex); + const todosQb = knex('todos'); + + describe('insertOne', () => { + it('should return the newly created record', async () => { + const todo = { + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + }; + + jest.spyOn(todosQb, 'insert'); + jest.spyOn(todosQb, 'returning'); + jest.spyOn(todosQb, 'then'); + jest.spyOn(todosQb, 'catch').mockImplementationOnce(async () => ({ + id: 1, + ...todo, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })); + + const result = await todos.insertOne(todo); + + expect(todosQb.insert).toHaveBeenCalledWith(todo); + expect(todosQb.returning).toHaveBeenCalledWith('*'); + expect(todosQb.then).toHaveBeenCalled(); + expect(todosQb.catch).toHaveBeenCalled(); + + expect(result).toEqual(expect.objectContaining(todo)); + }); + }); + + describe('findById', () => { + it('should retrun the first record found', async () => { + const todo: Todo = { + id: 1, + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + jest.spyOn(todosQb, 'where'); + jest + .spyOn(todosQb, 'first') + .mockImplementationOnce(() => Promise.resolve(todo) as never); + + const result = await todos.findById(todo.id, todo.userId); + + expect(todosQb.where).toHaveBeenCalledWith({ + id: todo.id, + userId: todo.userId, + }); + expect(todosQb.first).toHaveBeenCalled(); + + expect(result).toEqual(todo); + }); + }); + + describe('updateById', () => { + it('should perform a findById with empty payload', async () => { + const todo: Todo = { + id: 1, + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + jest.spyOn(todos, 'findById').mockImplementationOnce(async () => todo); + jest.spyOn(todosQb, 'update'); + + await todos.updateById(todo.id, todo.userId, {}); + + expect(todos.findById).toHaveBeenCalledWith(todo.id, todo.userId); + expect(todosQb.update).not.toHaveBeenCalled(); + }); + + it('should update the record and return it', async () => { + const todo: Todo = { + id: 1, + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + const input = { note: 'updated' }; + const updated = { ...todo, ...input }; + + jest.spyOn(todosQb, 'where'); + jest.spyOn(todosQb, 'update'); + jest.spyOn(todosQb, 'returning'); + jest.spyOn(todosQb, 'then'); + jest + .spyOn(todosQb, 'catch') + .mockImplementationOnce(() => Promise.resolve(updated) as never); + + const result = await todos.updateById(todo.id, todo.userId, input); + + expect(todosQb.where).toHaveBeenCalledWith({ + id: todo.id, + userId: todo.userId, + }); + expect(todosQb.update).toHaveBeenCalledWith(input); + expect(todosQb.returning).toHaveBeenCalledWith('*'); + expect(todosQb.then).toHaveBeenCalled(); + expect(todosQb.catch).toHaveBeenCalled(); + + expect(result).toEqual(updated); + }); + + it('should rethrow fk violation on userId as RecordNotFound', () => { + const error = new DatabaseError('fk violation', 72, 'error'); + error.code = PostgresError.FOREIGN_KEY_VIOLATION; + error.constraint = 'fk_todos_user_id'; + const thenable = () => ({ then: () => Promise.reject(error) }); + + jest + .spyOn(todosQb, 'returning') + .mockImplementationOnce(thenable as never); + + expect( + todos.insertOne({ + userId: '1', + name: 'name', + note: 'note', + completed: false, + }) + ).rejects.toThrow(new RecordNotFoundError('User not found')); + }); + }); + + describe('deleteById', () => { + it('should delete the record by ID', async () => { + jest.spyOn(todosQb, 'where'); + jest + .spyOn(todosQb, 'del') + .mockImplementationOnce(() => Promise.resolve(1) as never); + + const result = await todos.deleteById(1, '1'); + + expect(todosQb.where).toHaveBeenCalledWith({ id: 1, userId: '1' }); + expect(todosQb.del).toHaveBeenCalled(); + expect(result).toBe(1); + }); + }); + + describe('list', () => { + it('should list the records', async () => { + jest.spyOn(todosQb, 'where'); + jest.spyOn(todosQb, 'clone'); + jest.spyOn(todosQb, 'filter'); + jest.spyOn(todosQb, 'sort'); + jest + .spyOn(todosQb, 'paginate') + .mockImplementationOnce(() => Promise.resolve([]) as never); + jest + .spyOn(todosQb, 'count') + .mockImplementationOnce(() => Promise.resolve([{ count: 0 }]) as never); + + const userId = '1'; + const query = {}; + + await todos.list(userId, query); + + expect(todosQb.where).toHaveBeenCalledWith({ userId }); + expect(todosQb.clone).toHaveBeenCalledTimes(2); + expect(todosQb.filter).toHaveBeenCalledWith(undefined, { + name: filterByName, + completed: filterByCompleted, + }); + expect(todosQb.sort).toHaveBeenCalledWith(undefined, { + name: sortByName, + createdAt: sortByCreatedAt, + }); + expect(todosQb.count).toHaveBeenCalled(); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.ts new file mode 100644 index 00000000..2308862c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/repositories/todos.repository.ts @@ -0,0 +1,83 @@ +import { Knex } from 'knex'; +import { rethrowError } from '@utils/error'; +import { Paginated, extractPagination, parseCount } from '@utils/query'; +import { InsertTodo, ListTodosQuery, Todo, UpdateTodo } from '../entities'; +import { TodoUserNotFound } from '../error-mappings'; +import { filterByCompleted, filterByName } from '../filters'; +import { sortByCreatedAt, sortByName } from '../sorters'; + +export class TodosRepository { + constructor(private readonly knex: Knex) {} + + async insertOne(input: InsertTodo): Promise { + return await this.knex('todos') + .insert(input) + .returning('*') + .then(([todo]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async findById( + id: Todo['id'], + userId: Todo['userId'] + ): Promise { + return await this.knex('todos').where({ id, userId }).first(); + } + + async updateById( + id: Todo['id'], + userId: Todo['userId'], + input: UpdateTodo + ): Promise { + if (Object.keys(input).length === 0) { + return this.findById(id, userId); + } + + return await this.knex('todos') + .where({ id, userId }) + .update(input) + .returning('*') + .then(([todo]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async deleteById(id: Todo['id'], userId: Todo['userId']): Promise { + return await this.knex('todos').where({ id, userId }).del(); + } + + async list( + userId: Todo['userId'], + query: ListTodosQuery + ): Promise> { + const qb = this.knex('todos').where({ userId }); + + const data = await qb + .clone() + .filter(query.filters, { + name: filterByName, + completed: filterByCompleted, + }) + .sort(query.sorts, { + name: sortByName, + createdAt: sortByCreatedAt, + }) + .paginate(query.pagination); + + const total = await qb + .clone() + .filter(query.filters, { + name: filterByName, + completed: filterByCompleted, + }) + .count() + .then(parseCount); + + return { + data: data, + meta: { + ...extractPagination(query.pagination), + total, + }, + }; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/create.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/create.route.test.ts new file mode 100644 index 00000000..6578c1ff --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/create.route.test.ts @@ -0,0 +1,36 @@ +import { createTodoRoute as route } from './create.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const todosService = { create: jest.fn(() => todo) }; + const req = { + auth: { payload: { sub: '1' } }, + body: { name: 'Laundry' }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#create', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.create).toHaveBeenCalledWith({ + ...req.body, + userId: req.auth.payload.sub, + }); + }); + + it('should respond with 201', () => { + expect(status).toHaveBeenCalledWith(201); + }); + + it('should send the created todo', () => { + expect(send).toHaveBeenCalledWith(todo); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/create.route.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/create.route.ts new file mode 100644 index 00000000..15e45197 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/create.route.ts @@ -0,0 +1,40 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { createTodoSchema, todoSchema } from '../schemas'; + +export const createTodoRoute = defineRoute({ + operationId: 'todo-create', + summary: 'Create a To-Do', + description: 'Create a new To-Do item', + tags: ['v1', 'Todo'], + method: 'post', + path: '/', + authenticate: true, + request: { + body: createTodoSchema, + }, + responses: { + 201: todoSchema, + 401: response.Unauthorized, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler( + async ( + { + body, + services, + auth: { + payload: { sub }, + }, + }, + res + ) => { + const todo = await services.todosService.create({ + ...body, + userId: sub, + }); + + res.status(201).send(todo); + } + ) +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/delete.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/delete.route.test.ts new file mode 100644 index 00000000..5701308a --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/delete.route.test.ts @@ -0,0 +1,35 @@ +import { deleteTodoRoute as route } from './delete.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todosService = { delete: jest.fn(() => 1) }; + const req = { + auth: { payload: { sub: '1' } }, + params: { id: 1 }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#delete', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.delete).toHaveBeenCalledWith( + req.params.id, + req.auth.payload.sub + ); + }); + + it('should respond with 201', () => { + expect(status).toHaveBeenCalledWith(204); + }); + + it('should send nothing', () => { + expect(send).toHaveBeenCalled(); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/delete.route.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/delete.route.ts new file mode 100644 index 00000000..1f466377 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/delete.route.ts @@ -0,0 +1,38 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { todoIdParamSchema } from '../schemas'; + +export const deleteTodoRoute = defineRoute({ + operationId: 'todo-delete', + summary: 'Delete a To-Do', + description: 'Delete a To-Do item', + tags: ['v1', 'Todo'], + method: 'delete', + path: '/:id', + authenticate: true, + request: { + params: todoIdParamSchema, + }, + responses: { + 204: response.NoContent, + 401: response.Unauthorized, + 404: response.NotFound, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler( + async ( + { + params, + services, + auth: { + payload: { sub }, + }, + }, + res + ) => { + await services.todosService.delete(params.id, sub); + + res.status(204).send(); + } + ) +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/get.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/get.route.test.ts new file mode 100644 index 00000000..f84fde1e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/get.route.test.ts @@ -0,0 +1,32 @@ +import { getTodoRoute as route } from './get.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const todosService = { find: jest.fn(() => todo) }; + const req = { + auth: { payload: { sub: '1' } }, + params: { id: 1 }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#find', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.find).toHaveBeenCalledWith( + req.params.id, + req.auth.payload.sub + ); + }); + + it('should send the found todo', () => { + expect(send).toHaveBeenCalledWith(todo); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/get.route.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/get.route.ts new file mode 100644 index 00000000..302e2d7f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/get.route.ts @@ -0,0 +1,27 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { todoIdParamSchema, todoSchema } from '../schemas'; + +export const getTodoRoute = defineRoute({ + operationId: 'todo-get', + summary: 'Get a To-Do', + description: 'Get a To-Do item', + tags: ['v1', 'Todo'], + method: 'get', + path: '/:id', + authenticate: true, + request: { + params: todoIdParamSchema, + }, + responses: { + 200: todoSchema, + 401: response.Unauthorized, + 404: response.NotFound, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ services, params, auth: { payload: { sub } } }, res) => { + const todo = await services.todosService.find(params.id, sub); + + res.send(todo); + }) +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/index.ts new file mode 100644 index 00000000..68cf2650 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/index.ts @@ -0,0 +1,14 @@ +import { prefixRoutes } from '@utils/api'; +import { createTodoRoute } from './create.route'; +import { deleteTodoRoute } from './delete.route'; +import { getTodoRoute } from './get.route'; +import { listTodosRoute } from './list.route'; +import { updateTodoRoute } from './update.route'; + +export const todoRoutes = prefixRoutes('/v1/todos', [ + createTodoRoute, + deleteTodoRoute, + getTodoRoute, + listTodosRoute, + updateTodoRoute, +]); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/list.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/list.route.test.ts new file mode 100644 index 00000000..51d25121 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/list.route.test.ts @@ -0,0 +1,40 @@ +import { listTodosRoute as route } from './list.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const response = { + data: [todo], + meta: { + total: 1, + page: 1, + items: 20, + }, + }; + const todosService = { list: jest.fn(() => response) }; + const req = { + auth: { payload: { sub: '1' } }, + query: { filters: { name: 'Laundy' } }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#list', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.list).toHaveBeenCalledWith( + req.auth.payload.sub, + req.query + ); + }); + + it('should send the paginated todo list', () => { + expect(send).toHaveBeenCalledWith(response); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/list.route.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/list.route.ts new file mode 100644 index 00000000..11faf0e6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/list.route.ts @@ -0,0 +1,37 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { paginated } from '@utils/query'; +import { todoSchema, listTodosQuerySchema } from '../schemas'; + +export const listTodosRoute = defineRoute({ + operationId: 'todo-list', + summary: 'List To-Dos', + description: 'List To-Do items', + tags: ['v1', 'Todo'], + method: 'get', + path: '/', + authenticate: true, + request: { + query: listTodosQuerySchema, + }, + responses: { + 401: response.Unauthorized, + 200: paginated(todoSchema), + }, +}).attachHandler( + asyncHandler( + async ( + { + query, + services, + auth: { + payload: { sub }, + }, + }, + res + ) => { + const todos = await services.todosService.list(sub, query); + + res.send(todos); + } + ) +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/update.route.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/update.route.test.ts new file mode 100644 index 00000000..e200f1d6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/update.route.test.ts @@ -0,0 +1,34 @@ +import { updateTodoRoute as route } from './update.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const todosService = { update: jest.fn(() => todo) }; + const req = { + auth: { payload: { sub: '1' } }, + params: { id: 1 }, + body: { name: 'Laundry' }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#update', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.update).toHaveBeenCalledWith( + req.params.id, + req.auth.payload.sub, + req.body + ); + }); + + it('should send the updated todo', () => { + expect(send).toHaveBeenCalledWith(todo); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/routes/update.route.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/update.route.ts new file mode 100644 index 00000000..ea6d7a51 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/routes/update.route.ts @@ -0,0 +1,40 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { todoIdParamSchema, updateTodoSchema, todoSchema } from '../schemas'; + +export const updateTodoRoute = defineRoute({ + operationId: 'todo-update', + summary: 'Update a To-Do', + description: 'Update a To-Do item', + tags: ['v1', 'Todo'], + method: 'patch', + path: '/:id', + authenticate: true, + request: { + params: todoIdParamSchema, + body: updateTodoSchema, + }, + responses: { + 200: todoSchema, + 401: response.Unauthorized, + 404: response.NotFound, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler( + async ( + { + body, + params, + services, + auth: { + payload: { sub }, + }, + }, + res + ) => { + const todo = await services.todosService.update(params.id, sub, body); + + res.send(todo); + } + ) +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/create-todo.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/create-todo.schema.ts new file mode 100644 index 00000000..1bc8de02 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/create-todo.schema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; +import { todoSchema } from './todo.schema'; + +export const createTodoSchema = z.object({ + name: todoSchema.shape.name, + note: todoSchema.shape.note.optional(), + completed: todoSchema.shape.completed.optional().default(false), +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/index.ts new file mode 100644 index 00000000..ed4024ed --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/index.ts @@ -0,0 +1,5 @@ +export * from './create-todo.schema'; +export * from './list-todos-query.schema'; +export * from './todo-id-param.schema'; +export * from './todo.schema'; +export * from './update-todo.schema'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/list-todos-query.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/list-todos-query.schema.ts new file mode 100644 index 00000000..8ad2317a --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/list-todos-query.schema.ts @@ -0,0 +1,13 @@ +import { pagination, sorts } from '@utils/query'; +import { z } from 'zod'; + +export const listTodosQuerySchema = z.object({ + filters: z + .object({ + name: z.string().optional(), + completed: z.boolean().optional(), + }) + .optional(), + sorts: sorts(z.enum(['name', 'createdAt'])).optional(), + pagination: pagination.optional(), +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/todo-id-param.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/todo-id-param.schema.ts new file mode 100644 index 00000000..8d70952c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/todo-id-param.schema.ts @@ -0,0 +1,6 @@ +import z from 'zod'; +import { todoSchema } from './todo.schema'; + +export const todoIdParamSchema = z.object({ + id: todoSchema.shape.id.coerce(), +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/todo.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/todo.schema.ts new file mode 100644 index 00000000..aca444b7 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/todo.schema.ts @@ -0,0 +1,20 @@ +import { z } from 'zod'; +import { withId, withTimestamps } from '@utils/validation'; +import { userSchema } from '@api/users/schemas'; + +export const todoSchema = z + .object({ + userId: userSchema.shape.id, + name: z.string().trim().min(1).max(255).openapi({ example: 'Laundry' }), + note: z + .string() + .trim() + .min(1) + .max(255) + .nullable() + .openapi({ example: 'Buy detergent' }), + completed: z.boolean().openapi({ example: false }), + }) + .merge(withId) + .merge(withTimestamps) + .openapi({ ref: 'ToDo' }); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/update-todo.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/update-todo.schema.ts new file mode 100644 index 00000000..7a26911d --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/schemas/update-todo.schema.ts @@ -0,0 +1,9 @@ +import { todoSchema } from './todo.schema'; + +export const updateTodoSchema = todoSchema + .pick({ + name: true, + note: true, + completed: true, + }) + .partial(); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/services/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/services/index.ts new file mode 100644 index 00000000..df9135e2 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/services/index.ts @@ -0,0 +1 @@ +export * from './todos.service'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/services/todos.service.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/services/todos.service.test.ts new file mode 100644 index 00000000..4b048cb6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/services/todos.service.test.ts @@ -0,0 +1,124 @@ +import { RecordNotFoundError } from '@database/errors'; +import { Todo } from '../entities'; +import { TodosRepository } from '../repositories'; +import { TodosService } from './todos.service'; + +jest.mock('../repositories/todos.repository'); + +describe('TodoService', () => { + const todos = new TodosRepository({} as never); + const todosService = new TodosService(todos); + const userId = '1'; + + describe('create', () => { + it('should create the record', async () => { + const todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + expect(await todosService.find(todo.id, userId)).toBeDefined(); + }); + }); + + describe('find', () => { + describe('when the record does not exist', () => { + it('should throw an error', async () => { + await expect( + todosService.find(Date.now(), userId) + ).rejects.toThrow(RecordNotFoundError); + }); + }); + + describe('when the record exists', () => { + let todo: Todo; + + beforeAll(async () => { + todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + }); + + it('should return the record', async () => { + expect(await todosService.find(todo.id, userId)).toBeDefined(); + }); + }); + }); + + describe('update', () => { + describe('when the record does not exist', () => { + it('should throw an error', async () => { + await expect( + todosService.update(Date.now(), userId, { completed: true }) + ).rejects.toThrow(RecordNotFoundError); + }); + }); + + describe('when the record exists', () => { + let todo: Todo; + + beforeAll(async () => { + todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + }); + + it('should update the record', async () => { + const input = { name: 'new-name', note: 'new note', completed: true }; + const updated = await todosService.update(todo.id, userId, input); + expect(updated).toEqual(expect.objectContaining(input)); + }); + }); + }); + + describe('delete', () => { + describe('when the record does not exist', () => { + it('should throw an error', async () => { + await expect( + todosService.delete(Date.now(), userId) + ).rejects.toThrow(RecordNotFoundError); + }); + }); + + describe('when the record exists', () => { + let todo: Todo; + + beforeAll(async () => { + todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + }); + + it('should delete the record', async () => { + await todosService.delete(todo.id, userId); + await expect(todosService.find(todo.id, userId)).rejects.toThrow( + RecordNotFoundError + ); + }); + }); + }); + + describe('list', () => { + const userId = '2'; + + beforeAll(async () => { + await todosService.create({ userId, name: 'Abc 1', completed: false }); + await todosService.create({ userId, name: 'Abc 2', completed: true }); + await todosService.create({ userId, name: 'Zyx 1', completed: false }); + await todosService.create({ userId, name: 'Zyx 2', completed: true }); + await todosService.create({ userId, name: 'Zyx 3', completed: false }); + }); + + it('should list my toods', async () => { + const result = await todosService.list(userId, {}); + expect(result.data).toHaveLength(5); + expect(result.meta.total).toBe(5); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/services/todos.service.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/services/todos.service.ts new file mode 100644 index 00000000..7582b7f2 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/services/todos.service.ts @@ -0,0 +1,40 @@ +import { Paginated, definedOrNotFound, updatedOrNotFound } from '@utils/query'; +import { InsertTodo, ListTodosQuery, Todo, UpdateTodo } from '../entities'; +import { TodosRepository } from '../repositories'; + +export class TodosService { + constructor(private readonly todos: TodosRepository) {} + + create(input: InsertTodo): Promise { + return this.todos.insertOne(input); + } + + find(id: Todo['id'], userId: Todo['userId']): Promise { + return this.todos + .findById(id, userId) + .then(definedOrNotFound('To-Do not found')); + } + + update( + id: Todo['id'], + userId: Todo['userId'], + input: UpdateTodo + ): Promise { + return this.todos + .updateById(id, userId, input) + .then(definedOrNotFound('To-Do not found')); + } + + delete(id: Todo['id'], userId: Todo['userId']): Promise { + return this.todos + .deleteById(id, userId) + .then(updatedOrNotFound('To-Do not found')); + } + + list( + userId: Todo['userId'], + query: ListTodosQuery + ): Promise> { + return this.todos.list(userId, query); + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/index.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/index.ts new file mode 100644 index 00000000..18b954f5 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/index.ts @@ -0,0 +1,2 @@ +export * from './todos-by-created-at.sorter'; +export * from './todos-by-name.sorter'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts new file mode 100644 index 00000000..169221d7 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.test.ts @@ -0,0 +1,14 @@ +import { sortByCreatedAt } from './todos-by-created-at.sorter'; + +const queryBuilder = { + orderBy: jest.fn(), +}; + +describe('sortByCreatedAt', () => { + const order = 'asc'; + + it('should sort by createdAt', () => { + sortByCreatedAt(queryBuilder as never, order); + expect(queryBuilder.orderBy).toHaveBeenCalledWith('createdAt', order); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts new file mode 100644 index 00000000..039f5dbf --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-created-at.sorter.ts @@ -0,0 +1,5 @@ +import { Tables } from 'knex/types/tables'; +import { Sorter } from '@utils/query'; + +export const sortByCreatedAt: Sorter = (qb, order) => + qb.orderBy('createdAt', order); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts new file mode 100644 index 00000000..8db1a80f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.test.ts @@ -0,0 +1,14 @@ +import { sortByName } from './todos-by-name.sorter'; + +const queryBuilder = { + orderBy: jest.fn(), +}; + +describe('sortByName', () => { + const order = 'desc'; + + it('should sort by name', () => { + sortByName(queryBuilder as never, order); + expect(queryBuilder.orderBy).toHaveBeenCalledWith('name', order); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.ts b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.ts new file mode 100644 index 00000000..da9aab29 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/todos/sorters/todos-by-name.sorter.ts @@ -0,0 +1,5 @@ +import { Tables } from 'knex/types/tables'; +import { Sorter } from '@utils/query'; + +export const sortByName: Sorter = (qb, order) => + qb.orderBy('name', order); diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/entities/index.ts b/node-cli/assets/express/example-app-auth0/src/api/users/entities/index.ts new file mode 100644 index 00000000..6390588a --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/entities/index.ts @@ -0,0 +1,3 @@ +export * from './insert-user.entity'; +export * from './update-user.entity'; +export * from './user.entity'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/entities/insert-user.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/users/entities/insert-user.entity.ts new file mode 100644 index 00000000..36c73741 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/entities/insert-user.entity.ts @@ -0,0 +1,4 @@ +import { Insert } from '@database/operations'; +import { User } from './user.entity'; + +export type InsertUser = Insert>; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/entities/update-user.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/users/entities/update-user.entity.ts new file mode 100644 index 00000000..a83848a4 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/entities/update-user.entity.ts @@ -0,0 +1,4 @@ +import { Update } from '@database/operations'; +import { User } from './user.entity'; + +export type UpdateUser = Update>; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/entities/user.entity.ts b/node-cli/assets/express/example-app-auth0/src/api/users/entities/user.entity.ts new file mode 100644 index 00000000..296ba80b --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/entities/user.entity.ts @@ -0,0 +1,8 @@ +export interface User { + id: number; + userId: string; + email: string; + password: string | null; + createdAt: string; + updatedAt: string; +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/error-mappings/index.ts b/node-cli/assets/express/example-app-auth0/src/api/users/error-mappings/index.ts new file mode 100644 index 00000000..9e6f56e6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './user-email-taken.error-mapping'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/node-cli/assets/express/example-app-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts new file mode 100644 index 00000000..d3b5f779 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/error-mappings/user-email-taken.error-mapping.ts @@ -0,0 +1,6 @@ +import { uniqueViolation } from '@database/error-mappings'; + +export const UserEmailTaken = uniqueViolation( + 'unq_users_email', + 'User email already taken' +); diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/index.ts b/node-cli/assets/express/example-app-auth0/src/api/users/index.ts new file mode 100644 index 00000000..ec11d0a5 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/index.ts @@ -0,0 +1,4 @@ +export * from './entities'; +export * from './error-mappings'; +export * from './repositories'; +export * from './schemas'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/repositories/__mocks__/users.repository.ts b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/__mocks__/users.repository.ts new file mode 100644 index 00000000..46277b1e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/__mocks__/users.repository.ts @@ -0,0 +1,34 @@ +import { DuplicateRecordError } from '@database/errors'; +import { InsertUser, User } from '../../entities'; + +export class UsersRepository { + private lastId = 0; + private records: User[] = []; + + private nextId() { + return ++this.lastId; + } + + async insertOne(input: InsertUser): Promise { + const existingRecord = await this.findByEmail(input.email); + + if (existingRecord) { + throw new DuplicateRecordError('User Email already taken'); + } + + const record = { + id: this.nextId(), + ...input, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + this.records.push(record); + + return record; + } + + async findByEmail(email: string): Promise { + return this.records.find((r) => r.email === email); + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/repositories/index.ts b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/index.ts new file mode 100644 index 00000000..96798590 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/index.ts @@ -0,0 +1 @@ +export * from './users.repository'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/repositories/users.repository.test.ts b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/users.repository.test.ts new file mode 100644 index 00000000..8dcff4be --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/users.repository.test.ts @@ -0,0 +1,79 @@ +import Knex from 'knex'; +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { DuplicateRecordError } from '@database/errors'; +import { User } from '../entities/user.entity'; +import { UsersRepository } from './users.repository'; + +describe('UsersRepository', () => { + const knex = Knex({ client: 'pg', connection: {} }); + const users = new UsersRepository(knex); + const usersQb = knex('users'); + + describe('insertOne', () => { + it('should return the newly created record', async () => { + const user = { email: 'email@example.com', password: '123', userId: '1' }; + + jest.spyOn(usersQb, 'insert'); + jest.spyOn(usersQb, 'returning'); + jest.spyOn(usersQb, 'then'); + jest.spyOn(usersQb, 'catch').mockImplementationOnce(async () => ({ + id: 1, + ...user, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })); + + const result = await users.insertOne(user); + + expect(usersQb.insert).toHaveBeenCalledWith(user); + expect(usersQb.returning).toHaveBeenCalledWith('*'); + expect(usersQb.then).toHaveBeenCalled(); + expect(usersQb.catch).toHaveBeenCalled(); + + expect(result).toEqual(expect.objectContaining(user)); + }); + + it('should rethrow email uniq constraint violation as duplicate record', () => { + const error = new DatabaseError('unique violation', 72, 'error'); + error.code = PostgresError.UNIQUE_VIOLATION; + error.constraint = 'unq_users_email'; + const thenable = () => ({ then: () => Promise.reject(error) }); + + jest + .spyOn(usersQb, 'returning') + .mockImplementationOnce(thenable as never); + + expect( + users.insertOne({ email: 'email@example.com', password: '123', userId: '1' }) + ).rejects.toThrow( + new DuplicateRecordError('User email already taken') + ); + }); + }); + + describe('findByEmail', () => { + it('should return the first record found', async () => { + const user: User = { + id: 1, + email: 'email@example.com', + userId: '1', + password: '123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + jest.spyOn(usersQb, 'where'); + jest + .spyOn(usersQb, 'first') + .mockImplementationOnce(() => Promise.resolve(user) as never); + + const result = await users.findByEmail(user.email); + + expect(usersQb.where).toHaveBeenCalledWith({ email: user.email }); + expect(usersQb.first).toHaveBeenCalled(); + + expect(result).toEqual(user); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/repositories/users.repository.ts b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/users.repository.ts new file mode 100644 index 00000000..03874069 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/repositories/users.repository.ts @@ -0,0 +1,20 @@ +import { Knex } from 'knex'; +import { rethrowError } from '@utils/error'; +import { InsertUser, User } from '../entities'; +import { UserEmailTaken } from '../error-mappings'; + +export class UsersRepository { + constructor(private readonly knex: Knex) {} + + async insertOne(input: InsertUser): Promise { + return await this.knex('users') + .insert(input) + .returning('*') + .then(([user]) => user) + .catch(rethrowError(UserEmailTaken)); + } + + async findByEmail(email: User['email']): Promise { + return await this.knex('users').where({ email }).first(); + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/schemas/index.ts b/node-cli/assets/express/example-app-auth0/src/api/users/schemas/index.ts new file mode 100644 index 00000000..d0035833 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/schemas/index.ts @@ -0,0 +1,2 @@ +// TODO: check if this can be deleted +export * from './user.schema'; diff --git a/node-cli/assets/express/example-app-auth0/src/api/users/schemas/user.schema.ts b/node-cli/assets/express/example-app-auth0/src/api/users/schemas/user.schema.ts new file mode 100644 index 00000000..4d6a96de --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/api/users/schemas/user.schema.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; +import { email, password, withId, withTimestamps } from '@utils/validation'; + +export const userSchema = z + .object({ + email: email, + password: password, + userId: z.string(), + }) + .merge(withId) + .merge(withTimestamps) + .openapi({ ref: 'User' }); diff --git a/node-cli/assets/express/example-app-auth0/src/app.test.ts b/node-cli/assets/express/example-app-auth0/src/app.test.ts new file mode 100644 index 00000000..8b728946 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/app.test.ts @@ -0,0 +1,27 @@ +import { mockAxios } from '../test/utils/mock-axios'; +mockAxios(); + +import { create } from './app'; + +describe('create', () => { + it('should return an app instance and a destroy function', () => { + const result = create({ + PORT: 3000, + REQUEST_LOGGING: false, + ERROR_LOGGING: false, + AUTH0_ISSUER_URL: 'AUTH0_ISSUER_URL', + AUTH0_CLIENT_ID: 'AUTH0_CLIENT_ID', + AUTH0_AUDIENCE: 'AUTH0_AUDIENCE', + AUTH0_CLIENT_SECRET: 'AUTH0_CLIENT_SECRET', + NODE_ENV: 'test', + PGDATABASE: 'database', + PGHOST: 'localhost', + PGPASSWORD: 'password', + PGPORT: 5432, + PGUSER: 'user', + }); + + expect(result.app).toBeDefined(); + expect(result.destroy).toBeDefined(); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/app.ts b/node-cli/assets/express/example-app-auth0/src/app.ts new file mode 100644 index 00000000..412371b4 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/app.ts @@ -0,0 +1,138 @@ +import express, { json, Request, RequestHandler } from 'express'; +import helmet from 'helmet'; +import cors from 'cors'; +import compression from 'compression'; +import pino from 'pino'; +import queryType from 'query-types'; +import { NotFound, Conflict } from 'http-errors'; + +import { Environment } from '@utils/environment'; +import { + onInit as initDatabase, + create as createDbClient, + destroy as destroyDbClient, + RecordNotFoundError, + DuplicateRecordError, +} from '@database'; +import { + handleError, + mapError, + logRequest, + validateRequest, + attachServices, +} from '@middleware'; + +import { + Auth0Service, + AuthService, + authGuard, + authRoutes, +} from '@api/auth'; +import { healthcheckRoutes } from '@api/healthchecks'; +import { TodosRepository, TodosService, todoRoutes } from '@api/todos'; +import { UsersRepository } from '@api/users'; +import axios from 'axios'; + +export function create(env: Environment) { + // init modules + initDatabase(); + + // create a logger + const logger = pino({ + name: 'http', + ...(env.NODE_ENV === 'development' && { + transport: { + target: 'pino-pretty' + }, + }), + }); + + // create services + const dbClient = createDbClient(); + const usersRepository = new UsersRepository(dbClient); + const todosRepository = new TodosRepository(dbClient); + const auth0Service = new Auth0Service( + logger, + axios, + env + ); + const authService = new AuthService( + logger, + auth0Service, + usersRepository + ); + const todosService = new TodosService(todosRepository); + const services: Request['services'] = { todosService, authService }; + + // create the app + const app = express(); + + if (env.REQUEST_LOGGING) { + app.use(logRequest(logger)); + } + + app.use( + // add security HTTP headers + helmet(), + // enables CORS + cors(/* TODO: configure origins */), + // parses the body of application/json requests + json(), + // compresses response bodies + compression(), + // makes the services available to the route handlers by attaching them to the request + attachServices(services), + // handles numeric and boolean values for Express req.query object + queryType.middleware() + ); + + // flatten all routes into an array + // alternatively, a separate router instance can be used + // for each group of routes (group by prefix) + const routes = [ + ...healthcheckRoutes, + ...authRoutes, + ...todoRoutes, + ]; + + // register routes + for (const { + method, + path, + request, + authenticate = false, + middleware = [], + handler, + } of routes) { + if (authenticate) { + middleware.push(authGuard(env.AUTH0_AUDIENCE, env.AUTH0_ISSUER_URL)); + } + + if (request) { + middleware.push(validateRequest(request)); + } + + // eslint-disable-next-line security/detect-object-injection + app[method]( + path, + ...(middleware as RequestHandler[]), + handler as RequestHandler + ); + } + + // register error handlers + app.use( + mapError({ + [RecordNotFoundError.name]: NotFound, + [DuplicateRecordError.name]: Conflict + }), + handleError(logger, env.ERROR_LOGGING) + ); + + // define an app tear down function + const destroy = async () => { + await destroyDbClient(dbClient); + }; + + return { app, destroy }; +} diff --git a/node-cli/assets/express/example-app-auth0/src/database/client.test.ts b/node-cli/assets/express/example-app-auth0/src/database/client.test.ts new file mode 100644 index 00000000..53c371ad --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/client.test.ts @@ -0,0 +1,23 @@ +import Knex from 'knex'; +import { create, destroy } from './client'; + +describe('create', () => { + it('should construct a new client', () => { + create(); + expect(Knex).toHaveBeenCalledWith( + expect.objectContaining({ + client: 'pg', + connection: {}, + pool: { min: 0 }, + }) + ); + }); +}); + +describe('destroy', () => { + it('should call destroy on the client', () => { + const client = { destroy: jest.fn() }; + destroy(client as never); + expect(client.destroy).toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/database/client.ts b/node-cli/assets/express/example-app-auth0/src/database/client.ts new file mode 100644 index 00000000..92a56f98 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/client.ts @@ -0,0 +1,23 @@ +import Knex, { Knex as TKnex } from 'knex'; + +export const create = () => + Knex({ + client: 'pg', + // We don't need to specify the connection options as we're using the default env var names + // see https://node-postgres.com/features/connecting#environment-variables + // see https://www.postgresql.org/docs/9.1/libpq-envars.html + connection: {}, + pool: { + // the minimum is for all connections rather than alive, so it has to be set to zero + // see https://knexjs.org/guide/#pool + min: 0, + }, + migrations: { + directory: './migrations', + }, + seeds: { + directory: `./src/database/seeds/${process.env.NODE_ENV}`, + }, + }); + +export const destroy = (knex: TKnex) => knex.destroy(); diff --git a/node-cli/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts new file mode 100644 index 00000000..0e4a5eee --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts @@ -0,0 +1,46 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { RecordNotFoundError } from '../errors'; +import { foreignKeyViolation } from './foreign-key-violation.error-mapping'; + +describe('foreignKeyViolation', () => { + it('should return an error mapping', () => { + expect(foreignKeyViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = foreignKeyViolation( + 'constraint-name', + 'error-message' + ).isError; + + it('should return true when the error is a foreign key constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.FOREIGN_KEY_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a foreign key constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = foreignKeyViolation( + 'constraint-name', + 'error-message' + ).newError; + + it('should return an instance of RecordNotFound with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(RecordNotFoundError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts new file mode 100644 index 00000000..ede30de6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/foreign-key-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { RecordNotFoundError } from '@database/errors'; +import { isForeignKeyViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const foreignKeyViolation = ( + constraint: string, + message: string +): ErrorMapping => ({ + isError: isForeignKeyViolation(constraint), + newError: () => new RecordNotFoundError(message), +}); diff --git a/node-cli/assets/express/example-app-auth0/src/database/error-mappings/index.ts b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/index.ts new file mode 100644 index 00000000..9f743b83 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/index.ts @@ -0,0 +1,2 @@ +export * from './foreign-key-violation.error-mapping'; +export * from './unique-violation.error-mapping'; diff --git a/node-cli/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts new file mode 100644 index 00000000..ec0e98c0 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.test.ts @@ -0,0 +1,43 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { DuplicateRecordError } from '../errors'; +import { uniqueViolation } from './unique-violation.error-mapping'; + +describe('uniqueViolation', () => { + it('should return an error mapping', () => { + expect(uniqueViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = uniqueViolation('constraint-name', 'error-message').isError; + + it('should return true when the error is a unique constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.UNIQUE_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a unique constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = uniqueViolation( + 'constraint-name', + 'error-message' + ).newError; + + it('should return an instance of DuplicateRecord with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(DuplicateRecordError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.ts b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.ts new file mode 100644 index 00000000..df298502 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/error-mappings/unique-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { DuplicateRecordError } from '@database/errors'; +import { isUniqueViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const uniqueViolation = ( + constraint: string, + message: string +): ErrorMapping => ({ + isError: isUniqueViolation(constraint), + newError: () => new DuplicateRecordError(message), +}); diff --git a/node-cli/assets/express/example-app-auth0/src/database/errors/duplicate-record.error.ts b/node-cli/assets/express/example-app-auth0/src/database/errors/duplicate-record.error.ts new file mode 100644 index 00000000..65fbab07 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/errors/duplicate-record.error.ts @@ -0,0 +1,6 @@ +export class DuplicateRecordError extends Error { + constructor(message: string) { + super(message); + this.name = DuplicateRecordError.name; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/database/errors/index.ts b/node-cli/assets/express/example-app-auth0/src/database/errors/index.ts new file mode 100644 index 00000000..7099d226 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/errors/index.ts @@ -0,0 +1,2 @@ +export * from './duplicate-record.error'; +export * from './record-not-found.error'; diff --git a/node-cli/assets/express/example-app-auth0/src/database/errors/record-not-found.error.ts b/node-cli/assets/express/example-app-auth0/src/database/errors/record-not-found.error.ts new file mode 100644 index 00000000..52bcb4fa --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/errors/record-not-found.error.ts @@ -0,0 +1,6 @@ +export class RecordNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = RecordNotFoundError.name; + } +} diff --git a/node-cli/assets/express/example-app-auth0/src/database/index.ts b/node-cli/assets/express/example-app-auth0/src/database/index.ts new file mode 100644 index 00000000..4de1612b --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/index.ts @@ -0,0 +1,7 @@ +export * from './client'; +export * from './error-mappings'; +export * from './errors'; +export * from './on-init'; +export * from './operations'; +export * from './query-builder'; +export * from './utils'; diff --git a/node-cli/assets/express/example-app-auth0/src/database/on-init.ts b/node-cli/assets/express/example-app-auth0/src/database/on-init.ts new file mode 100644 index 00000000..1624cd1f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/on-init.ts @@ -0,0 +1,8 @@ +import pg from 'pg'; + +export const onInit = () => { + // https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js + pg.types.setTypeParser(pg.types.builtins.INT8, parseInt); + pg.types.setTypeParser(pg.types.builtins.NUMERIC, parseFloat); + pg.types.setTypeParser(pg.types.builtins.DATE, (v) => v); // keep as string for now +}; diff --git a/node-cli/assets/express/example-app-auth0/src/database/operations.ts b/node-cli/assets/express/example-app-auth0/src/database/operations.ts new file mode 100644 index 00000000..dd2cd033 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/operations.ts @@ -0,0 +1,14 @@ +import { NullableKeysPartial } from '@utils/types'; + +// Nullable means both that you can put a null and that the default is null. +// Having a default value means that the key can be omitted (i.e. is optional), +// hence, all nullables keys are optional +/** + * Makes nullable keys optional (partial) as well + */ +export type Insert = NullableKeysPartial; + +/** + * Makes all keys optional (partial) + */ +export type Update = Partial; diff --git a/node-cli/assets/express/example-app-auth0/src/database/query-builder/extensions.test.ts b/node-cli/assets/express/example-app-auth0/src/database/query-builder/extensions.test.ts new file mode 100644 index 00000000..a0c79519 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/query-builder/extensions.test.ts @@ -0,0 +1,78 @@ +import { Sort } from '@utils/query'; +import { filter, sort, paginate } from './extensions'; + +describe('filter', () => { + it('should apply all the filters to the query builder', () => { + const queryBuilder = {}; + + const filters = { + name: 'John', + height: undefined, + age: 42, + }; + + const filterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _name: string) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _age: number) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + height: jest.fn((qb, _height: number) => qb), + }; + + filter(queryBuilder as never, filters, filterMap as never); + + expect(filterMap.name).toHaveBeenCalledWith(queryBuilder, filters.name); + expect(filterMap.height).not.toHaveBeenCalled(); + expect(filterMap.age).toHaveBeenCalledWith(queryBuilder, filters.age); + }); +}); + +describe('sort', () => { + it('should apply all the sortings to the query builder', () => { + const queryBuilder = {}; + + const sorts: Sort<'name' | 'age'>[] = [ + { column: 'name' }, + { column: 'age', order: 'desc' }, + ]; + + const sorterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _order) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _order) => qb), + }; + + sort(queryBuilder as never, sorts, sorterMap); + + expect(sorterMap.name).toHaveBeenCalledWith(queryBuilder, 'asc'); + expect(sorterMap.age).toHaveBeenCalledWith(queryBuilder, 'desc'); + }); +}); + +describe('paginate', () => { + it('should apply an offset and a limit to the query builder', () => { + const queryBuilder = { + offset() { + return this; + }, + limit() { + return this; + }, + }; + + jest.spyOn(queryBuilder, 'offset'); + jest.spyOn(queryBuilder, 'limit'); + + const pagination = { + page: 3, + items: 15, + }; + + paginate(queryBuilder as never, pagination); + + expect(queryBuilder.offset).toHaveBeenCalledWith(30); + expect(queryBuilder.limit).toHaveBeenCalledWith(15); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/database/query-builder/extensions.ts b/node-cli/assets/express/example-app-auth0/src/database/query-builder/extensions.ts new file mode 100644 index 00000000..c6ece085 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/query-builder/extensions.ts @@ -0,0 +1,48 @@ +import { Knex } from 'knex'; +import { + Sort, + Pagination, + FilterMap, + SorterMap, + extractPagination, +} from '@utils/query'; + +// +// Knex.QueryBuilder extensions +// +export const filter = < + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + Query extends Record, + Filters extends FilterMap +>( + qb: Knex.QueryBuilder, + filters: Query, + filterMap: Filters +): Knex.QueryBuilder => + Object.entries(filters) + .filter(([, v]) => v !== undefined) + .reduce>( + (qb, [k, v]) => filterMap[k as keyof Filters](qb, v as never), + qb + ); + +// eslint-disable-next-line @typescript-eslint/ban-types +export const sort = ( + qb: Knex.QueryBuilder, + sorts: Sort[], + sorterMap: SorterMap +): Knex.QueryBuilder => + sorts.reduce>( + (qb, sort) => sorterMap[sort.column](qb, sort.order || 'asc'), + qb + ); + +export const paginate = ( + qb: QB, + pagination?: Pagination +) => { + const { page, items } = extractPagination(pagination); + + return qb.offset((page - 1) * items).limit(items); +}; diff --git a/node-cli/assets/express/example-app-auth0/src/database/query-builder/index.ts b/node-cli/assets/express/example-app-auth0/src/database/query-builder/index.ts new file mode 100644 index 00000000..4509f5cc --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/query-builder/index.ts @@ -0,0 +1 @@ +export * from './extensions'; diff --git a/node-cli/assets/express/example-app-auth0/src/database/seeds/test/index.ts b/node-cli/assets/express/example-app-auth0/src/database/seeds/test/index.ts new file mode 100644 index 00000000..fa2d4088 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/seeds/test/index.ts @@ -0,0 +1,38 @@ +import { Knex } from "knex"; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.seed = async function (knex: Knex) { + // Deletes ALL existing entries + await knex('users').insert([ + // The original password for this hash is 'pass@ord' + { + email: 'hello@email.com', + password: '$2b$10$Mxur7NOiTlm22yuldEMZgOCbIV7bxDCcUbBLFbzrJ1MrnIczZB.92', // pragma: allowlist secret + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); + + await knex('todos').insert([ + { + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 2', + note: 'Buy detergent 2', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 3', + note: 'Buy detergent 3', + completed: true, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); +}; diff --git a/node-cli/assets/express/example-app-auth0/src/database/utils/error.ts b/node-cli/assets/express/example-app-auth0/src/database/utils/error.ts new file mode 100644 index 00000000..38cf35ea --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/utils/error.ts @@ -0,0 +1,17 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; + +export const isDatabaseError = (e: unknown): e is DatabaseError => + e instanceof DatabaseError && + e.code !== undefined && + e.constraint !== undefined; + +export const isUniqueViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.UNIQUE_VIOLATION && + e.constraint === constraint; + +export const isForeignKeyViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.FOREIGN_KEY_VIOLATION && + e.constraint === constraint; diff --git a/node-cli/assets/express/example-app-auth0/src/database/utils/index.ts b/node-cli/assets/express/example-app-auth0/src/database/utils/index.ts new file mode 100644 index 00000000..93ae819e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/database/utils/index.ts @@ -0,0 +1 @@ +export * from './error'; diff --git a/node-cli/assets/express/example-app-auth0/src/extensions/knex/index.ts b/node-cli/assets/express/example-app-auth0/src/extensions/knex/index.ts new file mode 100644 index 00000000..a826b4c4 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/extensions/knex/index.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +import Knex from 'knex'; +import { filter, sort, paginate } from '@database/query-builder'; +import { FilterMap, Pagination, Sort, SorterMap } from '@utils/query'; + +declare module 'knex' { + namespace Knex { + interface QueryBuilder { + /** + * Given a column-to-value map and column-to-filter map, applies the filters to the query builder. + * Filter map must cover all columns in the column-to-value map. + * + * Example: + * ``` + * queryBuilder.filter( + * { + * name: 'John', + * isAncient: true, + * }, + * { + * name: (queryBuilder, name) => queryBuilder.whereILike('name', `%${name}%`), + * isAncient: (queryBuilder, isAncient) => queryBuilder.where('age', isAncient ? '>=' : '<', 100), + * } + * ); + * ``` + */ + filter( + filters: Filters | undefined, + filterMap: FilterMap + ): this; + /** + * Given a list of column sortings an a column-to-sorter map, applies the sorters to the query builder. + * Sorter map must cover all columns in the column sortings list. + * + * Example: + * ``` + * queryBuilder.sort( + * [ + * { column: 'name' }, + * { column: 'age', order: 'desc'} + * ], + * { + * name: (queryBuilder, order) => queryBuilder.orderBy('name', order), + * age: (queryBuilder, order) => queryBuilder.orderBy('age', order), + * } + * ); + * ``` + */ + sort( + sorts: Sort[] | undefined, + sorterMap: SorterMap + ): this; + /** + * Offset pagination. This a shorthand for the following: + * + * ``` + * queryBuilder + * .offset((pagination.page - 1) * pagination.items) + * .limit(pagination.items); + * ``` + */ + paginate(pagination?: Pagination): this; + } + } +} + +export const register = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.filter) { + Knex.QueryBuilder.extend('filter', function (filters, filterMap) { + return filters ? filter(this, filters, filterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.sort) { + Knex.QueryBuilder.extend('sort', function (sorts, sorterMap) { + return sorts ? sort(this, sorts || [], sorterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.paginate) { + Knex.QueryBuilder.extend('paginate', function (pagination) { + return paginate(this, pagination); + }); + } +}; diff --git a/node-cli/assets/express/example-app-auth0/src/extensions/knex/register.ts b/node-cli/assets/express/example-app-auth0/src/extensions/knex/register.ts new file mode 100644 index 00000000..07230200 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/extensions/knex/register.ts @@ -0,0 +1,3 @@ +import { register } from './index'; + +register(); diff --git a/node-cli/assets/express/example-app-auth0/src/extensions/zod/index.ts b/node-cli/assets/express/example-app-auth0/src/extensions/zod/index.ts new file mode 100644 index 00000000..5bead0fb --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/extensions/zod/index.ts @@ -0,0 +1,24 @@ +import z from 'zod'; +import { extendZodWithOpenApi } from 'zod-openapi'; + +declare module 'zod' { + interface ZodNumber { + coerce(): ZodNumber; + } +} + +export const register = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (typeof z.ZodNumber.prototype.coerce === 'undefined') { + z.ZodNumber.prototype.coerce = function () { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new (this as any).constructor({ + ...this._def, + coerce: true, + }); + }; + } + + extendZodWithOpenApi(z); +}; diff --git a/node-cli/assets/express/example-app-auth0/src/extensions/zod/register.ts b/node-cli/assets/express/example-app-auth0/src/extensions/zod/register.ts new file mode 100644 index 00000000..07230200 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/extensions/zod/register.ts @@ -0,0 +1,3 @@ +import { register } from './index'; + +register(); diff --git a/node-cli/assets/express/example-app-auth0/src/index.ts b/node-cli/assets/express/example-app-auth0/src/index.ts new file mode 100644 index 00000000..8923f99a --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/index.ts @@ -0,0 +1,45 @@ +// register extensions as the very first thing in the entry point +import '@extensions/zod/register'; +import '@extensions/knex/register'; + +import { createHttpTerminator } from 'http-terminator'; + +import { environmentSchema } from '@utils/environment'; +import { create as createApp } from './app'; + +function bootstrap() { + // validate environment and freeze it + const env = Object.freeze(environmentSchema.parse(process.env)); + + // create the application + const { app, destroy: destroyApp } = createApp(env); + + // start server + const server = app.listen(env.PORT, () => { + console.log(`App is running on http://localhost:${env.PORT}`); + }); + + // setup graceful shutdown + const httpTerminator = createHttpTerminator({ server }); + + const shutdown = async () => { + console.log('Shutting down...'); + + // process in-progress requests + await httpTerminator.terminate(); + + // then destroy the application + await destroyApp(); + }; + + const onSignal = (signal: NodeJS.Signals) => { + console.log(`${signal} received`); + shutdown(); + }; + + // attach signal listeners + process.on('SIGTERM', onSignal); + process.on('SIGINT', onSignal); +} + +bootstrap(); diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/attach-services.middleware.test.ts b/node-cli/assets/express/example-app-auth0/src/middleware/attach-services.middleware.test.ts new file mode 100644 index 00000000..e718dfb3 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/attach-services.middleware.test.ts @@ -0,0 +1,15 @@ +import { attachServices } from './attach-services.middleware'; + +describe('attachServices', () => { + it('should assign the services to the request', () => { + const services = {}; + const req = {}; + const next = jest.fn(); + + const middleware = attachServices(services as never); + middleware(req as never, {} as never, next); + + expect((req as { services: typeof services }).services).toBe(services); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/attach-services.middleware.ts b/node-cli/assets/express/example-app-auth0/src/middleware/attach-services.middleware.ts new file mode 100644 index 00000000..20bacf41 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/attach-services.middleware.ts @@ -0,0 +1,8 @@ +import { Request, RequestHandler } from 'express'; + +export const attachServices = + (services: Request['services']): RequestHandler => + (req, _res, next) => { + req.services = services; + next(); + }; diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/handle-error.middleware.test.ts b/node-cli/assets/express/example-app-auth0/src/middleware/handle-error.middleware.test.ts new file mode 100644 index 00000000..f8fd61b3 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/handle-error.middleware.test.ts @@ -0,0 +1,70 @@ +import createError from 'http-errors'; +import { handleError } from './handle-error.middleware'; + +describe('handleError', () => { + it('should return an error handling middleware', () => { + const middleware = handleError({} as never, false); + expect(middleware).toBeInstanceOf(Function); + expect(middleware.length).toBe(4); + }); + + describe('middleware', () => { + const middleware = handleError({} as never, false); + + describe('when the response headers have been sent', () => { + it('should call next with the error', () => { + const error = new Error(); + const next = jest.fn(); + middleware(error, {} as never, { headersSent: true } as never, next); + }); + }); + + describe('when the response headers have not been sent yet', () => { + describe('and the error is an http-errors error', () => { + const error = createError.UnprocessableEntity(); + error.errors = []; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + beforeAll(() => { + middleware(error, {} as never, res as never, () => undefined); + }); + + it('should set the response status to the error statusCode', () => { + expect(status).toHaveBeenCalledWith(error.statusCode); + }); + + it('should respond with an object with the error message', () => { + expect(send).toHaveBeenCalledWith({ + message: error.message, + errors: error.errors, + }); + }); + }); + + describe('but the error is not an http-errors error', () => { + const logger = { error: jest.fn() }; + const middleware = handleError(logger as never, true); + const error = new Error('message'); + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + beforeAll(() => { + middleware(error, {} as never, res as never, () => undefined); + }); + + it('should log the error', () => { + expect(logger.error).toHaveBeenCalledWith(error); + }); + + it('should respond with a 500 error with a predefined message', () => { + expect(send).toHaveBeenCalledWith({ + message: 'Internal Server Error', + }); + }); + }); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/handle-error.middleware.ts b/node-cli/assets/express/example-app-auth0/src/middleware/handle-error.middleware.ts new file mode 100644 index 00000000..5d8352cb --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/handle-error.middleware.ts @@ -0,0 +1,29 @@ +import { ErrorRequestHandler } from 'express'; +import { Logger } from 'pino'; +import createError from 'http-errors'; + +export const handleError = function (logger: Logger, logErrors: boolean): ErrorRequestHandler { + return function (err, _req, res, next) { + // https://expressjs.com/en/guide/error-handling.html + // If you call next() with an error after you have started writing the response (for example, if you encounter an error while streaming + // the response to the client) the Express default error handler closes the connection and fails the request. + // So when you add a custom error handler, you must delegate to the default Express error handler, when the headers have already + // been sent to the client + if (res.headersSent) { + return next(err); + } + + if (logErrors) { + logger.error(err); + } + + if (createError.isHttpError(err)) { + res.status(err.statusCode).send({ + message: err.message, + ...(err.errors && { errors: err.errors }), + }); + } else { + res.status(500).send({ message: 'Internal Server Error' }); + } + }; +}; diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/index.ts b/node-cli/assets/express/example-app-auth0/src/middleware/index.ts new file mode 100644 index 00000000..3f5a3fdb --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/index.ts @@ -0,0 +1,5 @@ +export * from './attach-services.middleware'; +export * from './handle-error.middleware'; +export * from './log-request.middleware'; +export * from './map-error.middleware'; +export * from './validate-request.middleware'; diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/log-request.middleware.test.ts b/node-cli/assets/express/example-app-auth0/src/middleware/log-request.middleware.test.ts new file mode 100644 index 00000000..9fb371d1 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/log-request.middleware.test.ts @@ -0,0 +1,33 @@ +import { logRequest } from './log-request.middleware'; +import { Logger } from 'pino'; +import { Request, Response } from 'express'; + +describe('logRequest', () => { + it('should log the request details', () => { + const logger: Partial = { info: jest.fn() }; + const req: Partial = { + method: 'GET', + url: '/todos', + body: { password: 'secret' }, + headers: { Authorization: 'Bearer token', Cookie: 'cookie' }, + ip: '127.0.0.1' + }; + const res: Partial = { on: jest.fn().mockImplementation((event, cb) => cb()) }; + const next = jest.fn(); + + logRequest(logger as Logger)(req as Request, res as Response, next); + + const expectedLogMsg = { + timestamp: expect.any(String), + duration: expect.any(String), + ip: req.ip, + headers: { Authorization: '[[REMOVED]]', Cookie: '[[REMOVED]]' }, + method: req.method, + url: req.url, + body: { password: '[[REMOVED]]' }, + }; + + expect(logger.info).toHaveBeenCalledWith(expectedLogMsg); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/log-request.middleware.ts b/node-cli/assets/express/example-app-auth0/src/middleware/log-request.middleware.ts new file mode 100644 index 00000000..ae259b15 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/log-request.middleware.ts @@ -0,0 +1,55 @@ +import { Request, Response, NextFunction } from 'express'; +import { IncomingHttpHeaders } from 'http'; +import { performance } from 'perf_hooks'; +import { Logger } from 'pino'; + +const REMOVED = '[[REMOVED]]'; + +export function sanitizeHeaders(headers: IncomingHttpHeaders): IncomingHttpHeaders { + const sanitizedHeaders = { ...headers }; + + if (sanitizedHeaders.Authorization) { + sanitizedHeaders.Authorization = REMOVED; + } + + if (sanitizedHeaders.Cookie) { + sanitizedHeaders.Cookie = REMOVED; + } + + return sanitizedHeaders; +} + +export function sanitizeBody(body: IncomingHttpHeaders): IncomingHttpHeaders { + const sanitizedBody = { ...body }; + + if (sanitizedBody.password) { + sanitizedBody.password = REMOVED; + } + + return sanitizedBody; +} + +export const logRequest = (logger: Logger) => (req: Request, res: Response, next: NextFunction) => { + const timestamp = new Date().toISOString(); + const startTime = performance.now(); + + res.on('finish', () => { + const endTime = performance.now(); + const duration = endTime - startTime; + const { url, body, method, headers, ip } = req; + + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: sanitizeHeaders(headers), + method, + url, + body: sanitizeBody(body), + }; + + logger.info(logMsg); + }); + + next(); +} diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/map-error.middleware.test.ts b/node-cli/assets/express/example-app-auth0/src/middleware/map-error.middleware.test.ts new file mode 100644 index 00000000..c67025f5 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/map-error.middleware.test.ts @@ -0,0 +1,32 @@ +import { DuplicateRecordError, RecordNotFoundError } from '@database/errors'; +import { mapError } from './map-error.middleware'; + +describe('mapError', () => { + it('should return an error handling middleware', () => { + const middleware = mapError({}); + expect(middleware).toBeInstanceOf(Function); + expect(middleware.length).toBe(4); + }); + + describe('middleware', () => { + it('should pass the error to next when no mapping for it is provided', () => { + const middleware = mapError({}); + const error = new Error(); + const next = jest.fn(); + middleware(error, {} as never, {} as never, next); + expect(next).toHaveBeenCalledWith(error); + }); + + it('should map the error and pass it to next when a mapping is provided', () => { + const middleware = mapError({ + [RecordNotFoundError.name]: DuplicateRecordError, + }); + const error = new RecordNotFoundError('message'); + const next = jest.fn(); + middleware(error, {} as never, {} as never, next); + expect(next).toHaveBeenCalledWith( + new DuplicateRecordError(new RecordNotFoundError('message').message) + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/map-error.middleware.ts b/node-cli/assets/express/example-app-auth0/src/middleware/map-error.middleware.ts new file mode 100644 index 00000000..f5534357 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/map-error.middleware.ts @@ -0,0 +1,12 @@ +import { ErrorRequestHandler } from 'express'; + +// TODO: utilize the mapError utility function +export const mapError = function ( + map: Record +): ErrorRequestHandler { + return function (err, _req, _res, next) { + const errorClass = map[err.name]; + const error = errorClass ? new errorClass(err.message) : err; + next(error); + }; +}; diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/validate-request.middleware.test.ts b/node-cli/assets/express/example-app-auth0/src/middleware/validate-request.middleware.test.ts new file mode 100644 index 00000000..bcd5c747 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/validate-request.middleware.test.ts @@ -0,0 +1,43 @@ +import z from 'zod'; +import { UnprocessableEntity } from 'http-errors'; +import { validateRequest } from './validate-request.middleware'; + +describe('validateJwt', () => { + describe('when the input is valid', () => { + const req = { body: { name: 'John', age: 42 } }; + const next = jest.fn(); + const schema = { + body: z.object({ name: z.string() }), + }; + const middleware = validateRequest(schema); + + beforeAll(() => { + middleware(req as never, {} as never, next); + }); + + it('should assign the validated input to the request', () => { + expect(req.body).toEqual({ name: 'John' }); + }); + + it('should call next', () => { + expect(next).toHaveBeenCalled(); + }); + }); + + describe('when the input is not vaid', () => { + const req = { body: { name: 42 } }; + const next = jest.fn(); + const schema = { + body: z.object({ name: z.string() }), + }; + const middleware = validateRequest(schema); + + beforeAll(() => { + middleware(req as never, {} as never, next); + }); + + it('should call next with the validation error', () => { + expect(next).toHaveBeenCalledWith(expect.any(UnprocessableEntity)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/middleware/validate-request.middleware.ts b/node-cli/assets/express/example-app-auth0/src/middleware/validate-request.middleware.ts new file mode 100644 index 00000000..f8e06ac5 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/middleware/validate-request.middleware.ts @@ -0,0 +1,24 @@ +import { RequestKey, RequestSchema } from '@utils/api'; +import { RequestHandler } from 'express'; +import createError from 'http-errors'; +import z from 'zod'; + +export const validateRequest = function ( + schema: RequestSchema +): RequestHandler { + return function (req, _res, next) { + const result = z.object(schema).safeParse(req); + + if (!result.success) { + const error = createError.UnprocessableEntity('Bad Data'); + error.errors = result.error.issues; + return next(error); + } + + for (const [k, v] of Object.entries(result.data)) { + req[k as RequestKey] = v; + } + + next(); + }; +}; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/handler.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/handler.test.ts new file mode 100644 index 00000000..b86cf0c2 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/handler.test.ts @@ -0,0 +1,20 @@ +import { asyncHandler } from './handler'; + +describe('asyncHandler', () => { + it('should pass the error to next when the promise returned by the handler is rejected', async () => { + const error = new Error(); + const next = jest.fn(); + const handler = asyncHandler(async () => { + throw error; + }); + await handler({} as never, {} as never, next); + expect(next).toHaveBeenCalledWith(error); + }); + + it('should not call next when the promise returned by the handler is resolved', async () => { + const next = jest.fn(); + const handler = asyncHandler(async () => null); + await handler({} as never, {} as never, next); + expect(next).not.toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/handler.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/handler.ts new file mode 100644 index 00000000..94846a80 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/handler.ts @@ -0,0 +1,67 @@ +import { + RequestHandler as ExpressRequestHandler, + ErrorRequestHandler as ExpressErrorHandler, +} from 'express'; +import { AnyZodObject, z } from 'zod'; + +export type RequestKey = 'params' | 'query' | 'headers' | 'body'; + +export type RequestSchema< + Params extends AnyZodObject = AnyZodObject, + Query extends AnyZodObject = AnyZodObject, + Body extends AnyZodObject = AnyZodObject, + Headers extends AnyZodObject = AnyZodObject +> = { + params?: Params; + query?: Query; + body?: Body; + headers?: Headers; +}; + +export type RequestHandler = + ExpressRequestHandler< + z.infer>, + unknown, + z.infer>, + z.infer> + >; + +export type ErrorHandler = + ExpressErrorHandler< + z.infer>, + unknown, + z.infer>, + z.infer> + >; + +/** + * A utility function that does the `.catch(next)` for you. + * This is, basically, `express-async-handler`: https://github.com/Abazhenov/express-async-handler/blob/master/index.js + * + * Example: + * ``` + * // this + * function (req, res, next) { + * service.create(req.body) + * .then(record => res.status(201).send(record)) + * .catch(next); + * } + * + * // turns into + * asyncHandler(async function(req, res) { + * const todo = await service.create(req.body); + * res.status(201).send(todo); + * }) + * ``` + */ +export const asyncHandler = < + S extends RequestSchema, + H extends RequestHandler +>( + handler: H +): RequestHandler => { + return function (req, res, next) { + const result = handler(req, res, next); + return Promise.resolve(result).catch(next); + }; +}; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/index.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/index.ts new file mode 100644 index 00000000..56860de4 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/index.ts @@ -0,0 +1,3 @@ +export * from './handler'; +export * from './route'; +export * from './response'; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/response.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/response.test.ts new file mode 100644 index 00000000..29db8e03 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/response.test.ts @@ -0,0 +1,52 @@ +import { ZodString, ZodObject } from 'zod'; +import { response } from './response'; + +describe('response', () => { + describe('NoContent', () => { + it('should be an OpenAPI response without content', () => { + const noContent = response.NoContent; + expect(noContent).toHaveProperty('description'); + expect(noContent).not.toHaveProperty('content'); + }); + }); + + describe('NotFound', () => { + it('should return a zod schema for an error', () => { + const schema = response.NotFound; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error' }; + expect(schema.parse(error)).toEqual(error); + }); + }); + + describe('Conflict', () => { + it('should return a zod schema for an error', () => { + const schema = response.Conflict; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error' }; + expect(schema.parse(error)).toEqual(error); + }); + }); + + describe('UnprocessableEntity', () => { + it('should return a zod schema for an error', () => { + const schema = response.UnprocessableEntity; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error', errors: [] }; + expect(schema.parse(error)).toEqual(error); + }); + }); + + describe('Unauthorized', () => { + it('should return a zod schema for an error', () => { + const schema = response.Unauthorized; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error' }; + expect(schema.parse(error)).toEqual(error); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/response.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/response.ts new file mode 100644 index 00000000..eaea92d4 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/response.ts @@ -0,0 +1,23 @@ +import z from 'zod'; +import httpStatuses from 'statuses'; + +const error = (message: string) => + z.object({ message: z.string().openapi({ example: message }) }); + +const zodErrorIssue = z.object({ + code: z.string().openapi({ example: 'invalid_type' }), + expected: z.string().openapi({ example: 'string' }), + received: z.string().openapi({ example: 'number' }), + path: z.array(z.string()).openapi({ example: ['address', 'zip'] }), + message: z.string().openapi({ example: 'Expected string, received number' }), +}); + +export const response = { + NoContent: { description: httpStatuses.message[204] as string }, + Unauthorized: error('Unauthorized').openapi({ ref: 'UnauthorizedError' }), + NotFound: error('Record not found').openapi({ ref: 'NotFoundError' }), + Conflict: error('Record already exists').openapi({ ref: 'ConflictError' }), + UnprocessableEntity: error('Invalid input') + .extend({ errors: z.array(zodErrorIssue) }) + .openapi({ ref: 'UnprocessableEntityError' }), +}; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/route.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/route.test.ts new file mode 100644 index 00000000..537edf97 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/route.test.ts @@ -0,0 +1,40 @@ +import { defineRoute, prefixRoute, prefixRoutes } from './route'; + +describe('prefixRoute', () => { + it("should prefix the route's path", () => { + expect(prefixRoute('/users', { path: '/create' }).path).toBe( + '/users/create' + ); + }); +}); + +describe('prefixRoutes', () => { + it("should prefix the routes' path", () => { + const routes = [{ path: '/create' }, { path: '/update' }]; + + expect(prefixRoutes('/users', routes)).toEqual([ + { path: '/users/create' }, + { path: '/users/update' }, + ]); + }); +}); + +describe('defineRoute', () => { + it('should return an object with a attachHandler function', () => { + const routeDefinition = {}; + const result = defineRoute(routeDefinition as never); + expect(result).toHaveProperty('attachHandler'); + expect(result.attachHandler).toBeInstanceOf(Function); + }); + + describe('attachHandler', () => { + it('should add a handler property to the route definition', () => { + const routeDefinition = {}; + const handler = () => undefined; + const route = defineRoute(routeDefinition as never).attachHandler( + handler + ); + expect(route.handler).toBe(handler); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/api/route.ts b/node-cli/assets/express/example-app-auth0/src/utils/api/route.ts new file mode 100644 index 00000000..afea387b --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/api/route.ts @@ -0,0 +1,43 @@ +import { ZodOpenApiResponseObject } from 'zod-openapi'; +import { RequestSchema, RequestHandler } from './handler'; +import { RequestHandler as ExpressRequestHandler } from 'express'; +import { AnyZodObject } from 'zod'; + +type Responses = { + [statusCode: string]: ZodOpenApiResponseObject | AnyZodObject; +}; + +export type RouteDefinition = { + operationId: string; + summary?: string; + description?: string; + tags?: string[]; + method: 'get' | 'put' | 'post' | 'delete' | 'patch'; + path: string; + authenticate?: boolean; + request?: S; + middleware?: Array>; + handler: RequestHandler; + responses: Responses; +}; + +export const prefixRoute = ( + prefix: string, + route: R +): R => ({ + ...route, + path: `${prefix}${route.path}`, +}); + +export const prefixRoutes = ( + prefix: string, + routes: R[] +): R[] => routes.map((route) => prefixRoute(prefix, route)); + +export const defineRoute = ( + route: Omit, 'handler'> +) => ({ + attachHandler: ( + handler: RouteDefinition['handler'] + ): RouteDefinition => ({ ...route, handler }), +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/environment.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/environment.test.ts new file mode 100644 index 00000000..fa56f67c --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/environment.test.ts @@ -0,0 +1,7 @@ +import { environmentSchema } from './environment'; + +describe('envSchema', () => { + it('should be defined', () => { + expect(environmentSchema).toBeDefined(); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/environment.ts b/node-cli/assets/express/example-app-auth0/src/utils/environment.ts new file mode 100644 index 00000000..c7a12b6d --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/environment.ts @@ -0,0 +1,32 @@ +import { z } from 'zod'; + +const string = z.string().trim(); +const port = z.coerce.number().int().gte(1024).lte(65535); +const secret = z.string().trim().min(10); + +export const environmentSchema = z.object({ + // Node + NODE_ENV: z.enum(['development', 'test', 'production']), + + // HTTP + PORT: port, + + REQUEST_LOGGING: z.enum(['true', 'false']).transform((value) => value === 'true'), + ERROR_LOGGING: z.enum(['true', 'false']).transform((value) => value === 'true'), + + // PostgreSQL + // TODO: this limits your options, should be revisited + PGHOST: string, + PGPORT: port, + PGUSER: string, + PGPASSWORD: string, + PGDATABASE: string, + + // JWT + AUTH0_ISSUER_URL: string, + AUTH0_CLIENT_ID: string, + AUTH0_AUDIENCE: string, + AUTH0_CLIENT_SECRET: secret, +}); + +export type Environment = Readonly>; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/error.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/error.test.ts new file mode 100644 index 00000000..562fc526 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/error.test.ts @@ -0,0 +1,59 @@ +import { ErrorMapping, mapError, rethrowError } from './error'; + +describe('mapError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const newError = new TypeError('type-error'); + + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return newError; + }, + }; + + it('should return the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(mapError(mapping)(error)).toBe(error); + }); + + it('should return the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(mapError(mapping)(error)).toBe(newError); + }); + }); +}); + +describe('rethrowError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return new TypeError('type-error'); + }, + }; + + it('should rethrow the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(() => rethrowError(mapping)(error)).toThrow(error); + }); + + it('should rethrow the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(() => rethrowError(mapping)(error)).toThrow( + new TypeError('type-error') + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/error.ts b/node-cli/assets/express/example-app-auth0/src/utils/error.ts new file mode 100644 index 00000000..df376a09 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/error.ts @@ -0,0 +1,28 @@ +type IsErrorPredicate = (error: unknown) => boolean; + +type ErrorFactory = () => Error; + +export interface ErrorMapping { + isError: IsErrorPredicate; + newError: ErrorFactory; +} + +export const mapError = function (...mappings: ErrorMapping[]) { + return function (err: Error) { + for (const { isError, newError } of mappings) { + if (isError(err)) { + return newError(); + } + } + + return err; + }; +}; + +export const rethrowError = function (...mappings: ErrorMapping[]) { + const errorMapper = mapError(...mappings); + + return function (err: Error) { + throw errorMapper(err); + }; +}; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/openapi.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/openapi.test.ts new file mode 100644 index 00000000..7ce68e8e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/openapi.test.ts @@ -0,0 +1,178 @@ +import z from 'zod'; +import { generateDocument, reformatPathParams } from './openapi'; +import { response, RouteDefinition } from '@utils/api'; +import { ZodOpenApiRequestBodyObject } from 'zod-openapi'; + +describe('reformatPathParams', () => { + it('should change the format of the path params', () => { + expect(reformatPathParams('/users/:userId/posts/:postId')).toBe( + '/users/{userId}/posts/{postId}' + ); + }); +}); + +describe('generateDocument', () => { + const route: RouteDefinition = { + operationId: 'route-id', + summary: 'Does something', + description: 'It does somehting', + tags: ['tag'], + method: 'post', + path: '/users/:id', + handler: (_req, res) => { + res.send('OK'); + }, + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + name: { + type: 'string', + example: 'Laundry', + }, + }, + required: ['name'], + }, + }, + }, + }, + }, + }; + + it('should define bearer token authentication', () => { + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [], + }); + + expect(document.components?.securitySchemes?.bearerAuth).toEqual({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + }); + }); + + describe('when the route requires authentication', () => { + const routeWithAuth: RouteDefinition = { + ...route, + authenticate: true, + }; + + it('should add security definitions', () => { + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithAuth], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect(definition?.security).toEqual([ + { + bearerAuth: [], + }, + ]); + }); + }); + + describe('when a route has responses schemas', () => { + it('should not try to convert non-zod response schemas', () => { + const routeWithJsonSchemaResponse: RouteDefinition = { + ...route, + responses: { + 204: response.NoContent, + }, + }; + + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithJsonSchemaResponse], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect(definition?.responses['204']).toEqual({ + description: 'No Content', + }); + }); + + it('should map zod schemas to application/json content schemas', () => { + const routeWithJsonSchemaResponse: RouteDefinition = { + ...route, + request: { + body: z.object({ + id: z.number().int(), + name: z.string(), + }), + }, + responses: { + 200: z.object({ + id: z.number().int(), + name: z.string(), + }), + }, + }; + + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithJsonSchemaResponse], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect( + (definition?.requestBody as ZodOpenApiRequestBodyObject)['content'] + ).toHaveProperty('application/json'); + expect(definition?.responses['200'].content).toHaveProperty( + 'application/json' + ); + }); + + it('should provide a default description when one is not available for the status code', () => { + const routeWithDefaultDescription: RouteDefinition = { + ...route, + responses: { + 1234: z.object({}), + }, + }; + + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithDefaultDescription], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect(definition?.responses['1234'].description).toBe( + 'Unknown response code' + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/openapi.ts b/node-cli/assets/express/example-app-auth0/src/utils/openapi.ts new file mode 100644 index 00000000..ce3a3f33 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/openapi.ts @@ -0,0 +1,107 @@ +import { ZodType } from 'zod'; +import { + ZodOpenApiVersion, + createDocument, + ZodOpenApiObject, + ZodOpenApiPathsObject, +} from 'zod-openapi'; +import { RouteDefinition } from '@utils/api'; +import httpStatuses from 'statuses'; + +/** + * Changes the path param placeholder syntax from `:param` to `{param}` + */ +export const reformatPathParams = (path: string) => + path.replaceAll(/:[a-zA-Z]+/g, (match) => `{${match.substring(1)}}`); + +/** + * Maps our route definitions to OpenAPI path definitions + */ +export const mapRouteToPath = ({ + request, + responses, + ...route +}: RouteDefinition) => ({ + operationId: route.operationId, + summary: route.summary, + description: route.description, + tags: route.tags, + ...(route.authenticate && { + security: [ + { + bearerAuth: [], + }, + ], + }), + ...(request && { + requestParams: { + path: request.params, + query: request.query, + header: request.headers, + }, + }), + ...(request?.body && { + requestBody: { + content: { + 'application/json': { + schema: request.body, + }, + }, + }, + }), + responses: Object.fromEntries( + Object.entries(responses).map(([code, schema]) => [ + code, + schema instanceof ZodType + ? { + description: + httpStatuses.message[code as never as number] ?? + 'Unknown response code', + content: { 'application/json': { schema } }, + } + : schema, + ]) + ), +}); + +/** + * Maps our route definitions to an OpenAPI paths object definition + */ +export const mapRoutesToPaths = (routes: RouteDefinition[]) => + routes.reduce((paths, route) => { + const path = reformatPathParams(route.path); + // eslint-disable-next-line security/detect-object-injection + paths[path] ||= {}; + // eslint-disable-next-line security/detect-object-injection + paths[path][route.method] = mapRouteToPath(route); + return paths; + }, {} as ZodOpenApiPathsObject); + +/** + * Generates the OpenAPI document + */ +export const generateDocument = ({ + version, + info, + routes, +}: { + // 3.1.0 is not yet supported by our version of swagger-ui + version: Exclude; + info: ZodOpenApiObject['info']; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + routes: RouteDefinition[]; +}) => + createDocument({ + openapi: version, + info, + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + }, + }, + }, + paths: mapRoutesToPaths(routes), + }); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/error.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/error.test.ts new file mode 100644 index 00000000..da408b48 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/error.test.ts @@ -0,0 +1,47 @@ +import { RecordNotFoundError } from '@database/errors'; +import { definedOrNotFound, updatedOrNotFound } from './error'; + +describe('definedOrNotFound', () => { + it('should return a function', () => { + expect(typeof definedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the value it is called with when the value is not undefined', () => { + expect(definedOrNotFound('message')(null)).toBe(null); + expect(definedOrNotFound('message')(false)).toBe(false); + expect(definedOrNotFound('message')(0)).toBe(0); + expect(definedOrNotFound('message')('')).toBe(''); + const emptyArray: unknown[] = []; + expect(definedOrNotFound('message')(emptyArray)).toBe(emptyArray); + const emptyObject = {}; + expect(definedOrNotFound('message')(emptyObject)).toEqual(emptyObject); + }); + + it('should throw RecordNotFound when the value it is called with is undefined', () => { + expect(() => definedOrNotFound('message')(undefined)).toThrow( + new RecordNotFoundError('message') + ); + }); + }); +}); + +describe('updatedOrNotFound', () => { + it('should return a function', () => { + expect(typeof updatedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the number it is called with when the number is not zero', () => { + expect(updatedOrNotFound('message')(-1)).toBe(-1); + expect(updatedOrNotFound('message')(1)).toBe(1); + expect(updatedOrNotFound('message')(NaN)).toEqual(NaN); + }); + + it('should throw RecordNotFound when the value it is called with is zero', () => { + expect(() => updatedOrNotFound('message')(0)).toThrow( + new RecordNotFoundError('message') + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/error.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/error.ts new file mode 100644 index 00000000..b903287e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/error.ts @@ -0,0 +1,25 @@ +import { RecordNotFoundError } from '@database/errors'; + +const definedOrThrow = (errorFactory: () => Error) => { + return (result: T | undefined): T => { + if (result === undefined) { + throw errorFactory(); + } + return result; + }; +}; + +const updatedOrThrow = (errorFactory: () => Error) => { + return (result: number): number => { + if (result === 0) { + throw errorFactory(); + } + return result; + }; +}; + +export const definedOrNotFound = (message: string) => + definedOrThrow(() => new RecordNotFoundError(message)); + +export const updatedOrNotFound = (message: string) => + updatedOrThrow(() => new RecordNotFoundError(message)); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/filtration.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/filtration.ts new file mode 100644 index 00000000..849cae26 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/filtration.ts @@ -0,0 +1,51 @@ +import { Knex } from 'knex'; + +/** + * A Filter is a function that accepts a QueryBuilder for a given entity and a value, + * applies the filter to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type FilterByName = Filter, Todo['name']>; + * + * const filterByName: FilterByName = (qb, name) => qb.where({ name }); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Filter = ( + qb: Knex.QueryBuilder, + value: Value +) => Knex.QueryBuilder; + +/** + * A FilerMap is a map is a mapping from query param names to filters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all query parameters have a corresponding filter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * // This would typically be obtained via z.infer + * interface ListTodosFilters { + * id?: number; + * name?: string; + * }; + * + * const listTodosFilterMap: FilterMap, ListTodosFilters> = { + * id: (qb, id) => qb.where({ id }), + * name: (qb, name) => qb.whereILike('name', `%${name}%`), + * }; + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type FilterMap = { + // Since query params are usually optional, + // we loop through the query params removing their optionality via `-?`, + // then map them to a filter of their value excluding undefined. + [K in keyof Filters]-?: Filter>; +}; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/index.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/index.ts new file mode 100644 index 00000000..794eb80f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/index.ts @@ -0,0 +1,5 @@ +export * from './error'; +export * from './filtration'; +export * from './pagination'; +export * from './result'; +export * from './sorting'; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/pagination.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/pagination.test.ts new file mode 100644 index 00000000..a9a54987 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/pagination.test.ts @@ -0,0 +1,45 @@ +import z, { ZodArray, ZodObject, ZodNumber } from 'zod'; +import { paginated, extractPagination, paginationDefaults } from './pagination'; + +describe('paginated', () => { + it('should return a new schema that describes a paginated resuls', () => { + const schema = z.number(); + const paginatedSchema = paginated(schema); + + expect(paginatedSchema.shape).toEqual( + expect.objectContaining({ + data: expect.any(ZodArray), + meta: expect.any(ZodObject), + }) + ); + + expect(paginatedSchema.shape.data.element).toBe(schema); + + expect(paginatedSchema.shape.meta.shape).toEqual( + expect.objectContaining({ + total: expect.any(ZodNumber), + page: expect.any(ZodNumber), + items: expect.any(ZodNumber), + }) + ); + }); +}); + +describe('extractPagination', () => { + it('should return the provided pagination', () => { + const pagination = { page: 1, items: 10 }; + expect(extractPagination(pagination)).toEqual(pagination); + }); + + it('should supply the defaults for any missing pagination properties', () => { + expect(extractPagination()).toEqual(paginationDefaults); + expect(extractPagination({ page: 1 })).toEqual({ + ...paginationDefaults, + page: 1, + }); + expect(extractPagination({ items: 10 })).toEqual({ + ...paginationDefaults, + items: 10, + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/pagination.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/pagination.ts new file mode 100644 index 00000000..0b1e0e2e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/pagination.ts @@ -0,0 +1,35 @@ +import { z, ZodType } from 'zod'; + +// TODO: move to a config or smth +export const paginationDefaults = { + page: 1, + items: 20, +}; + +export const pagination = z + .object({ + page: z.coerce.number().int().positive(), + items: z.coerce.number().int().positive(), + }) + .partial(); + +const paginationMeta = pagination.required().extend({ + total: z.number().int().nonnegative(), +}); + +export type Pagination = z.infer; +export type PaginationMeta = z.infer; + +export interface Paginated { + data: Entity[]; + meta: PaginationMeta; +} + +export const paginated = >(schema: Schema) => + z.object({ + data: z.array(schema), + meta: paginationMeta, + }); + +export const extractPagination = (pagination?: Pagination) => + Object.assign({}, paginationDefaults, pagination); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/result.test.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/result.test.ts new file mode 100644 index 00000000..6cf8a427 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/result.test.ts @@ -0,0 +1,11 @@ +import { parseCount } from './result'; + +describe('parseCount', () => { + it('should extract and parse the count when the count is a string', () => { + expect(parseCount([{ count: '10' }])).toBe(10); + }); + + it('should extract the count when the count is a number', () => { + expect(parseCount([{ count: 10 }])).toBe(10); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/result.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/result.ts new file mode 100644 index 00000000..2466eb02 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/result.ts @@ -0,0 +1,4 @@ +export type CountResult = Array<{ count?: string | number }>; + +export const parseCount = ([{ count }]: CountResult): number => + typeof count === 'string' ? parseInt(count) : (count as number); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/query/sorting.ts b/node-cli/assets/express/example-app-auth0/src/utils/query/sorting.ts new file mode 100644 index 00000000..03a9680a --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/query/sorting.ts @@ -0,0 +1,65 @@ +import { Knex } from 'knex'; +import z, { ZodEnum } from 'zod'; + +export const sortOrder = z.enum(['asc', 'desc']); + +export const sorts = >( + schema: Schema +) => + z.array( + z.object({ + column: schema, + order: sortOrder.optional(), + }) + ); + +export type SortOrder = z.infer; + +export interface Sort { + column: SortColumn; + order?: SortOrder; +} + +/** + * A Sorter is a function that accepts a QueryBuilder for a given entity and a sort order, + * applies the sorting to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type SortByName = Sorter>; + * + * const sortByName: SortByName = (qb, order) => qb.orderBy('name', order); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Sorter = ( + qb: Knex.QueryBuilder, + order?: SortOrder +) => Knex.QueryBuilder; + +/** + * A SorterMap is a map is a mapping from query param names to sorters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all sort parameters have a corresponding sorter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type ListTodoSortColumn = 'name' | 'createdAt'; + * + * const listTodosSorterMap: SorterMap, ListTodoSortColumn> = { + * name: (qb, order) => qb.orderBy('name', order), + * createdAt: (qb, order) => qb.orderBy('createdAt', order), + * }; + * ``` + */ +export type SorterMap< + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + SortColumn extends string +> = Record>; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/types.ts b/node-cli/assets/express/example-app-auth0/src/utils/types.ts new file mode 100644 index 00000000..1e2f37af --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/types.ts @@ -0,0 +1,7 @@ +export type IsNullable = null extends T ? K : never; + +export type NullableKeys = { [K in keyof T]: IsNullable }[keyof T]; + +/** `Partial` just for the nullable keys */ +export type NullableKeysPartial = Omit> & + Partial>>; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/validation/attribute-sets.ts b/node-cli/assets/express/example-app-auth0/src/utils/validation/attribute-sets.ts new file mode 100644 index 00000000..0b552a25 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/validation/attribute-sets.ts @@ -0,0 +1,15 @@ +import z from 'zod'; +import { id as idSchema, timestamp } from './attributes'; + +export const withId = z.object({ + id: idSchema, +}); + +export type WithId = z.infer; + +export const withTimestamps = z.object({ + createdAt: timestamp, + updatedAt: timestamp, +}); + +export type WithTimestamps = z.infer; diff --git a/node-cli/assets/express/example-app-auth0/src/utils/validation/attributes.ts b/node-cli/assets/express/example-app-auth0/src/utils/validation/attributes.ts new file mode 100644 index 00000000..e0fd83d2 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/validation/attributes.ts @@ -0,0 +1,22 @@ +import { z } from 'zod'; + +export const id = z.number().int().positive().openapi({ example: 1 }); + +export const email = z + .string() + .trim() + .toLowerCase() + .max(255) + .email() + .openapi({ example: 'john@mail.com' }); + +export const password = z + .string() + .min(6) + .max(255) + .openapi({ example: 'MyS3cr37Pass' }); + +export const timestamp = z + .string() + .datetime() + .openapi({ example: '2023-02-28T14:39:24.086Z' }); diff --git a/node-cli/assets/express/example-app-auth0/src/utils/validation/index.ts b/node-cli/assets/express/example-app-auth0/src/utils/validation/index.ts new file mode 100644 index 00000000..3b3a6a23 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/src/utils/validation/index.ts @@ -0,0 +1,2 @@ +export * from './attribute-sets'; +export * from './attributes'; diff --git a/node-cli/assets/express/example-app-auth0/test/healthchecks/live.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/healthchecks/live.spec-e2e.ts new file mode 100644 index 00000000..e0f12844 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/healthchecks/live.spec-e2e.ts @@ -0,0 +1,24 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +import request from 'supertest'; +import { create as createApp } from '../utils/app'; + +describe('GET /healthz/live', () => { + let app: Express.Application; + let destroy: () => Promise; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return 200', async () => { + await request(app).get('/healthz/live').expect(200); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/healthchecks/ready.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/healthchecks/ready.spec-e2e.ts new file mode 100644 index 00000000..2ae48dfa --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/healthchecks/ready.spec-e2e.ts @@ -0,0 +1,24 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +import request from 'supertest'; +import { create as createApp } from '../utils/app'; + +describe('GET /healthz/ready', () => { + let app: Express.Application; + let destroy: () => Promise; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return 200', async () => { + await request(app).get('/healthz/ready').expect(200); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/jest-e2e.config.js b/node-cli/assets/express/example-app-auth0/test/jest-e2e.config.js new file mode 100644 index 00000000..1f7737d8 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/jest-e2e.config.js @@ -0,0 +1,70 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + // run test via Nodejs + testEnvironment: 'node', + // needed for TypeScript + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + // root dir is the app root + rootDir: '..', + // match anything that is spec-e2e or test-e2e with a js, jsx, ts and tsx extension + testMatch: [ + "**/?(*.)+(spec|test)-e2e.[tj]s?(x)" + ], + // does some setup before each test file + setupFiles: [ + '/test/jest.setup.ts' + ], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, + // disable unit test manual mocks + modulePathIgnorePatterns: [ + '/__mocks__', + '/src/.*/__mocks__' + ], + // change coverage directory name so that it doesn't overlap with unit test coverage + coverageDirectory: '/coverage-e2e', + // coverage is collected from files under src/ + collectCoverageFrom: [ + '/src/api/**/*.[tj]s?(x)', + ], + // and from those files ignore + coveragePathIgnorePatterns: [ + // entry points + '/src/index.[tj]sx?$', + '/src/app.[tj]sx?$', + // extensions + '/src/extensions', + // module indexes + '/.*/index.[tj]sx?$', + // unit tests + '(spec|test).[tj]sx?$', + // type definitions + '/src/@types/', + // database migrations + '/src/modules/database/migrations', + // utility code + '/src/common', + '/src/modules/database/utils', + // middleware + '/src/api/middleware' + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, +}; diff --git a/node-cli/assets/express/example-app-auth0/test/jest.setup.ts b/node-cli/assets/express/example-app-auth0/test/jest.setup.ts new file mode 100644 index 00000000..e34878f0 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/jest.setup.ts @@ -0,0 +1,7 @@ +import '@extensions/zod/register'; +import '@extensions/knex/register'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/node-cli/assets/express/example-app-auth0/test/todos/create.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/todos/create.spec-e2e.ts new file mode 100644 index 00000000..a389598b --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/todos/create.spec-e2e.ts @@ -0,0 +1,96 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +const authMock = jest.fn(); + +jest.mock('express-oauth2-jwt-bearer', () => ({ + auth: jest.fn(() => authMock), +})); + +import request from 'supertest'; +import { + create as createApp, + expectError, + getTodoPayload, + Unauthorized, + UnprocessableEntity +} from '../utils'; +import { Knex } from 'knex'; +import { create } from '@database'; +import createError from 'http-errors'; + +describe('POST /v1/todos', () => { + let app: Express.Application; + let destroy: () => Promise; + const jwtTokens = { + idToken: 'token', + }; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + authMock.mockImplementation( + ( + req: { auth: { payload: { sub: string } } }, + res, + next: () => true + ) => { + req.auth = { + payload: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + }, + }; + return next(); + } + ); + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return the created todo', async () => { + const todoPayload = getTodoPayload(); + + const res = await request(app) + .post('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .send(todoPayload) + + expect(res.body.name).toEqual(todoPayload.name); + expect(res.body.note).toEqual(todoPayload.note); + expect(res.body.completed).toEqual(todoPayload.completed); + }); + + it('given an empty payload, should return 422 error', async () => { + await request(app) + .post('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .send({}) + .expect(expectError(UnprocessableEntity)); + }); + + it('should return 401 error when the user is not authenticated', async () => { + authMock.mockImplementation((request, response, next) => { + next(createError(401, 'No authorization token was found')) + }); + + await request(app) + .post('/v1/todos') + .set('Content-Type', 'application/json') + .send(getTodoPayload()) + .expect(expectError(Unauthorized)); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/todos/delete.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/todos/delete.spec-e2e.ts new file mode 100644 index 00000000..515f44be --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/todos/delete.spec-e2e.ts @@ -0,0 +1,88 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +const authMock = jest.fn(); + +jest.mock('express-oauth2-jwt-bearer', () => ({ + auth: jest.fn(() => authMock), +})); + +import request from 'supertest'; +import { + create as createApp, + expectError, + Unauthorized, + TodoNotFound +} from '../utils'; +import { Knex } from 'knex'; +import { create } from '@database'; +import createError from 'http-errors'; + +describe('DELETE /v1/todos/:id', () => { + let app: Express.Application; + let destroy: () => Promise; + const jwtTokens = { + idToken: 'token', + }; + const todoId = 1; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + authMock.mockImplementation( + ( + req: { auth: { payload: { sub: string } } }, + res, + next: () => true + ) => { + req.auth = { + payload: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + }, + }; + return next(); + } + ); + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return 204 when todo is deleted', async () => { + await request(app) + .delete(`/v1/todos/${todoId}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .expect(204); + }); + + it('should return 404 when given not existing todo id', async () => { + await request(app) + .delete(`/v1/todos/${Date.now()}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .expect(expectError(TodoNotFound)); + }); + + it('should return 401 error when user is not authenticated', async () => { + authMock.mockImplementation((request, response, next) => { + next(createError(401, 'No authorization token was found')) + }); + + await request(app) + .delete(`/v1/todos/${todoId}`) + .set('Content-Type', 'application/json') + .expect(expectError(Unauthorized)); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/todos/get.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/todos/get.spec-e2e.ts new file mode 100644 index 00000000..db49959f --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/todos/get.spec-e2e.ts @@ -0,0 +1,115 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +const authMock = jest.fn(); + +jest.mock('express-oauth2-jwt-bearer', () => ({ + auth: jest.fn(() => authMock), +})); + +import request from 'supertest'; +import { + create as createApp, + expectError, + TodoNotFound, + Unauthorized, + UnprocessableEntity, +} from '../utils'; +import { Todo } from '@api/todos'; +import { Knex } from 'knex'; +import { create } from '@database'; +import createError from 'http-errors'; + +describe('GET /v1/todos/:id', () => { + let app: Express.Application; + let destroy: () => Promise; + let dbClient: Knex; + const todo: Partial = { + id: 1, + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }; + const jwtTokens = { + idToken: 'token', + }; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + authMock.mockImplementation( + ( + req: { auth: { payload: { sub: string } } }, + res, + next: () => true + ) => { + req.auth = { + payload: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + }, + }; + return next(); + } + ); + }); + + afterAll(async () => { + await destroy(); + }); + + describe('when user is authenticated', () => { + describe('given todo id in the query', () => { + it('should return the todo', async () => { + const res = await request(app) + .get(`/v1/todos/${todo.id}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.id).toEqual(todo.id); + expect(res.body.name).toEqual(todo.name); + expect(res.body.note).toEqual(todo.note); + expect(res.body.completed).toEqual(todo.completed); + }); + }); + + describe('given not existing todo id in the query', () => { + it('should return 404', async () => { + await request(app) + .get(`/v1/todos/${Date.now()}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(expectError(TodoNotFound)); + }); + }); + + describe('given a text id in the query', () => { + it('should return 422 error', async () => { + await request(app) + .get(`/v1/todos/test`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(expectError(UnprocessableEntity)); + }); + }); + }); + + describe('when user is not authenticated', () => { + it('should return 401 error', async () => { + authMock.mockImplementation((request, response, next) => { + next(createError(401, 'No authorization token was found')) + }); + + await request(app) + .get(`/v1/todos/${todo.id}`) + .expect(expectError(Unauthorized)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/todos/list.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/todos/list.spec-e2e.ts new file mode 100644 index 00000000..e4d206f6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/todos/list.spec-e2e.ts @@ -0,0 +1,236 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +const authMock = jest.fn(); + +jest.mock('express-oauth2-jwt-bearer', () => ({ + auth: jest.fn(() => authMock), +})); + +import request from 'supertest'; +import { + create as createApp, + expectError, + getTodoPayload, + sortByField, + Unauthorized, +} from '../utils'; +import { Knex } from 'knex'; +import { create } from '@database'; +import createError from 'http-errors'; + +describe('GET /v1/todos', () => { + let app: Express.Application; + let destroy: () => Promise; + const jwtTokens = { + idToken: 'token', + }; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + authMock.mockImplementation( + ( + req: { auth: { payload: { sub: string } } }, + res, + next: () => true + ) => { + req.auth = { + payload: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + }, + }; + return next(); + } + ); + }); + + afterAll(async () => { + jest.clearAllMocks(); + await destroy(); + }); + + describe('when user is authenticated', () => { + describe('when no filters are applied', () => { + it('should return whole todos list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(3); + }); + }); + + describe('when completed=true filter is applied', () => { + it('should return completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?filters[completed]=true') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(1); + }); + }); + + describe('when completed=false filter is applied', () => { + it('should return not completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?filters[completed]=false') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(2); + }); + }); + + describe('when name = "Laundry" filter is applied', () => { + it('should return todo list with matched results', async () => { + const res = await request(app) + .get('/v1/todos?filters[name]=Laundry') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(3); + }); + }); + + describe('when pagination items is applied', () => { + it('should return not completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?pagination[items]=2') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(2); + }); + }); + + describe('when pagination page is applied', () => { + it('should return not completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?pagination[page]=2&pagination[items]=3') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(0); + expect(res.body.meta.total).toEqual(3); + }); + }); + + describe('when sort by name desc', () => { + it('should return desc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=name&sorts[0][order]=desc') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'name', 'desc') + ); + }); + }); + + describe('when sort by name asc', () => { + it('should return asc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=name&sorts[0][order]=asc') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'name', 'asc') + ); + }); + }); + + describe('when sort by createdAt desc', () => { + it('should return desc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=createdAt&sorts[0][order]=desc') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'createdAt', 'desc') + ); + }); + }); + + describe('when sort by createdAt asc', () => { + it('should return asc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=createdAt') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'createdAt', 'asc') + ); + }); + }); + + describe('when all filters are applied', () => { + it('should return filtered todo list', async () => { + const res = await request(app) + .get( + '/v1/todos?filters[name]=Laundry&sorts[0][column]=name&sorts[0][order]=desc&pagination[page]=1&pagination[items]=2' + ) + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(res.headers['content-type']).toMatch(/json/); + expect(res.status).toEqual(200); + expect(res.body.data.length).toEqual(2); + expect(res.body.meta.total).toEqual(3); + }); + }); + }); + + describe('when user is not authenticated', () => { + it('should return 401 error', async () => { + authMock.mockImplementation((request, response, next) => { + next(createError(401, 'No authorization token was found')) + }); + + await request(app) + .get('/v1/todos') + .send(getTodoPayload()) + .expect('content-type', /json/) + .expect(expectError(Unauthorized)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/todos/update.spec-e2e.ts b/node-cli/assets/express/example-app-auth0/test/todos/update.spec-e2e.ts new file mode 100644 index 00000000..dfd83294 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/todos/update.spec-e2e.ts @@ -0,0 +1,136 @@ +import { mockAxios } from '../utils/mock-axios'; +mockAxios() + +const authMock = jest.fn(); + +jest.mock('express-oauth2-jwt-bearer', () => ({ + auth: jest.fn(() => authMock), +})); + +import request from 'supertest'; +import { + create as createApp, + expectError, + getTodoPayload, + TodoNotFound, + Unauthorized, + UnprocessableEntity, +} from '../utils'; +import { Knex } from 'knex'; +import { create } from '@database'; +import createError from 'http-errors'; + +describe('POST /v1/todos/:id', () => { + let app: Express.Application; + let destroy: () => Promise; + let dbClient: Knex; + const todoId = 1; + const jwtTokens = { + idToken: 'token', + }; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + authMock.mockImplementation( + ( + req: { auth: { payload: { sub: string } } }, + res, + next: () => true + ) => { + req.auth = { + payload: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + }, + }; + return next(); + } + ); + }); + + afterAll(async () => { + await destroy(); + }); + + describe('when user is authenticated', () => { + describe('given the todo payload and id in the query', () => { + it('should return the updated todo', async () => { + const todoPayload = getTodoPayload(); + + const res = await request(app) + .patch(`/v1/todos/${todoId}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .send(todoPayload) + .expect(200); + + expect(res.body.name).toEqual(todoPayload.name); + expect(res.body.note).toEqual(todoPayload.note); + expect(res.body.completed).toEqual(todoPayload.completed); + }); + }); + + describe('given an empty payload and id in the query', () => { + it('should return the not updated todo', async () => { + const todo = await dbClient('todos').where({ id: todoId }).first(); + if (!todo) { + throw new Error('Todo not found'); + } + + const res = await request(app) + .patch(`/v1/todos/${todoId}`) + .send({}) + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(res.status).toEqual(200); + expect(res.body.id).toEqual(todo.id); + expect(res.body.name).toEqual(todo.name); + expect(res.body.note).toEqual(todo.note); + expect(res.body.completed).toEqual(todo.completed); + expect(res.body.userId).toEqual(todo.userId); + }); + }); + + describe('given not existing todo id in the query', () => { + it('should return 404', async () => { + await request(app) + .patch(`/v1/todos/${Date.now()}`) + .send(getTodoPayload()) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect('content-type', /json/) + .expect(expectError(TodoNotFound)); + }); + }); + + describe('given a text id in the query', () => { + it('should return 422 error', async () => { + await request(app) + .patch(`/v1/todos/test`) + .send(getTodoPayload()) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(expectError(UnprocessableEntity)); + }); + }); + }); + + describe('when user is not authenticated', () => { + it('should return 401 error', async () => { + authMock.mockImplementation((request, response, next) => { + next(createError(401, 'No authorization token was found')) + }); + + await request(app) + .patch(`/v1/todos/${todoId}`) + .send(getTodoPayload()) + .expect(expectError(Unauthorized)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app-auth0/test/utils/app.ts b/node-cli/assets/express/example-app-auth0/test/utils/app.ts new file mode 100644 index 00000000..2c572c01 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/app.ts @@ -0,0 +1,7 @@ +import { environmentSchema } from '@utils/environment'; +import { create as createApp } from '../../src/app'; + +export const create = () => { + const env = Object.freeze(environmentSchema.parse(process.env)); + return createApp(env); +}; diff --git a/node-cli/assets/express/example-app-auth0/test/utils/errors.ts b/node-cli/assets/express/example-app-auth0/test/utils/errors.ts new file mode 100644 index 00000000..25d445b3 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/errors.ts @@ -0,0 +1,16 @@ +import { HttpException } from './http-exception'; + +export const Unauthorized = (message = 'No authorization token was found') => + new HttpException(401, message); + +export const UnprocessableEntity = (message = 'Bad Data') => + new HttpException(422, message); + +export const TodoNotFound = (message = 'To-Do not found') => + new HttpException(404, message); + +export const UserConflict = (message = 'User email already taken') => + new HttpException(409, message); + +export const InvalidCredentials = (message = 'Invalid email or password') => + new HttpException(422, message); diff --git a/node-cli/assets/express/example-app-auth0/test/utils/expect-error.ts b/node-cli/assets/express/example-app-auth0/test/utils/expect-error.ts new file mode 100644 index 00000000..c94debc6 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/expect-error.ts @@ -0,0 +1,25 @@ +import { Response } from 'supertest'; +import { + InvalidCredentials, + TodoNotFound, + Unauthorized, + UnprocessableEntity, + UserConflict, +} from './errors'; +import { HttpException } from './http-exception'; + +const httpExceptionMap = { + [Unauthorized.name]: Unauthorized(), + [UnprocessableEntity.name]: UnprocessableEntity(), + [TodoNotFound.name]: TodoNotFound(), + [UserConflict.name]: UserConflict(), + [InvalidCredentials.name]: InvalidCredentials(), +}; + +export const expectError = + (ex: (message?: string) => HttpException) => (res: Response) => { + const error = httpExceptionMap[ex.name]; + expect(res.error).toBeDefined(); + expect(res.body.message).toEqual(error.message); + expect(res.status).toEqual(error.status); + }; diff --git a/node-cli/assets/express/example-app-auth0/test/utils/get-todo-payload.ts b/node-cli/assets/express/example-app-auth0/test/utils/get-todo-payload.ts new file mode 100644 index 00000000..7fda6f62 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/get-todo-payload.ts @@ -0,0 +1,9 @@ +import { Todo } from '@api/todos'; + +export const getTodoPayload: ( + completed?: boolean +) => Pick = (completed) => ({ + name: 'Laundry' + Date.now(), + note: 'Buy detergent' + Date.now(), + completed: completed ?? false, +}); diff --git a/node-cli/assets/express/example-app-auth0/test/utils/http-exception.ts b/node-cli/assets/express/example-app-auth0/test/utils/http-exception.ts new file mode 100644 index 00000000..f90512cb --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/http-exception.ts @@ -0,0 +1,7 @@ +export class HttpException extends Error { + constructor(public status: number, public message: string) { + super(message); + this.status = status; + this.message = message; + } +} diff --git a/node-cli/assets/express/example-app-auth0/test/utils/index.ts b/node-cli/assets/express/example-app-auth0/test/utils/index.ts new file mode 100644 index 00000000..5ab69612 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/index.ts @@ -0,0 +1,7 @@ +export * from './errors'; + +export { create } from './app'; +export { getTodoPayload } from './get-todo-payload'; +export { sortByField } from './sortby-field-todos'; +export { expectError } from './expect-error'; +export { HttpException } from './http-exception'; diff --git a/node-cli/assets/express/example-app-auth0/test/utils/mock-axios.ts b/node-cli/assets/express/example-app-auth0/test/utils/mock-axios.ts new file mode 100644 index 00000000..b01ffea2 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/mock-axios.ts @@ -0,0 +1,9 @@ +export const axiosMock = { + post: jest + .fn() + .mockImplementation(() => + Promise.resolve({ data: { access_token: 'token' } }) + ), +}; + +export const mockAxios = () => jest.mock('axios', () => axiosMock); diff --git a/node-cli/assets/express/example-app-auth0/test/utils/sortby-field-todos.ts b/node-cli/assets/express/example-app-auth0/test/utils/sortby-field-todos.ts new file mode 100644 index 00000000..a2537c6e --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/test/utils/sortby-field-todos.ts @@ -0,0 +1,19 @@ +import { Todo } from '@api/todos/entities'; +import { SortOrder } from '@utils/query'; + +type ListTodoSortColumn = 'name' | 'createdAt'; + +export const sortByField: ( + list: Todo[], + field: ListTodoSortColumn, + order?: SortOrder +) => Todo[] = (list, field, order) => { + return list.sort((a, b) => { + if (order === 'desc') { + // eslint-disable-next-line security/detect-object-injection + return b[field].localeCompare(a[field]); + } + // eslint-disable-next-line security/detect-object-injection + return a[field].localeCompare(b[field]); + }); +}; diff --git a/node-cli/assets/express/example-app-auth0/tsconfig.json b/node-cli/assets/express/example-app-auth0/tsconfig.json new file mode 100644 index 00000000..a4cca151 --- /dev/null +++ b/node-cli/assets/express/example-app-auth0/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "@tsconfig/recommended/tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "baseUrl": ".", + "moduleResolution": "node", + "resolveJsonModule": true, + "skipLibCheck": false, + "lib": ["ES2021"], + "paths": { + "@api/*": ["src/api/*"], + "@database": ["src/database"], + "@database/*": ["src/database/*"], + "@extensions/*": ["src/extensions/*"], + "@middleware": ["src/middleware"], + "@utils/*": ["src/utils/*"] + } + }, + "ts-node": { + "files": true, + "require": ["tsconfig-paths/register", "dotenv/config"] + } +} diff --git a/assets/express/example-app/.gitignore b/node-cli/assets/express/example-app/.gitignore similarity index 100% rename from assets/express/example-app/.gitignore rename to node-cli/assets/express/example-app/.gitignore diff --git a/node-cli/assets/express/example-app/.ls-lint.yml b/node-cli/assets/express/example-app/.ls-lint.yml new file mode 100644 index 00000000..15fa6973 --- /dev/null +++ b/node-cli/assets/express/example-app/.ls-lint.yml @@ -0,0 +1,29 @@ +ls: + # directories + .dir: kebab-case | regex:__tests__ | regex:__mocks__ | regex:__data__ + + # shell scripts + .sh: kebab-case + + # source files + # this is essentially kebab-case.with-custom.extentions.js + .js: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + .ts: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + + # migrations + migrations: + .ts: snake_case + +# ignored directories and files +ignore: + - node_modules + - .git + - .github + - .vscode + - .husky + - dist + - .commitlintrc.js + - .eslintrc.js + - .prettierrc.js + - .openapi + - src/@types diff --git a/assets/nest/example-app-pg/.openapi/gitignorefile b/node-cli/assets/express/example-app/.openapi/gitignorefile similarity index 100% rename from assets/nest/example-app-pg/.openapi/gitignorefile rename to node-cli/assets/express/example-app/.openapi/gitignorefile diff --git a/node-cli/assets/express/example-app/Dockerfile b/node-cli/assets/express/example-app/Dockerfile new file mode 100644 index 00000000..1ba37caf --- /dev/null +++ b/node-cli/assets/express/example-app/Dockerfile @@ -0,0 +1,79 @@ +# +# Create an intermediate image to build the application that will have devDependencies +# +FROM node:18-alpine AS builder + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Create a directory under the node user +RUN mkdir /app && chown node:node /app + +# Set the working directory +WORKDIR /app + +# Switch to the predefined node use to avoid running as root +USER node + +# Copy both package.json and package-lock.json +COPY --chown=node:node package*.json ./ + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier + +# Copy the source code +COPY --chown=node:node . /app + +# Build the app +RUN npm run build + +# +# This will be the image used in production with just the build and prod dependencies +# +FROM node:18-alpine + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Create a directory under the node user +RUN mkdir -p /usr/src/app && chown node:node /usr/src/app + +# Set the working directory +WORKDIR /usr/src/app + +# Switch to the predefined node use to avoid running as root +USER node + +# Copy both package.json and package-lock.json from the builder image +COPY --from=builder --chown=node:node /app/package*.json ./ + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # skip dev dependencies + --omit=dev \ + # rebuild bcrypt + && npm rebuild bcrypt \ + # attempt to reduce image size + && npm cache clean --force + +# Copy the build from the builder image +COPY --from=builder --chown=node:node /app/dist/ ./dist/ + +# Run +CMD [ "node", "./dist/index.js" ] diff --git a/node-cli/assets/express/example-app/__mocks__/knex.ts b/node-cli/assets/express/example-app/__mocks__/knex.ts new file mode 100644 index 00000000..06c77c90 --- /dev/null +++ b/node-cli/assets/express/example-app/__mocks__/knex.ts @@ -0,0 +1,70 @@ +const newQueryBuilder = () => ({ + where() { + return this; + }, + first() { + return this; + }, + update() { + return this; + }, + del() { + return this; + }, + insert() { + return this; + }, + returning() { + return this; + }, + count() { + return this; + }, + clone() { + return this; + }, + filter() { + return this; + }, + sort() { + return this; + }, + paginate() { + return this; + }, + list() { + return this; + }, + then() { + return this; + }, + catch() { + return this; + }, +}); + +const mocks: Record> = {}; + +const knex = jest.fn((name: string) => { + // eslint-disable-next-line security/detect-object-injection + return (mocks[name] ||= newQueryBuilder()); +}); + +interface KnexType { + (): typeof knex; + QueryBuilder: { + extend(): void; + }; +} + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +const Knex: KnexType = jest.fn(() => knex); + +Knex.QueryBuilder = { + extend() { + return; + }, +}; + +export default Knex; diff --git a/node-cli/assets/express/example-app/docker-compose.override.example.yml b/node-cli/assets/express/example-app/docker-compose.override.example.yml new file mode 100644 index 00000000..40deb5e0 --- /dev/null +++ b/node-cli/assets/express/example-app/docker-compose.override.example.yml @@ -0,0 +1,12 @@ +services: + + # uncomment the lines below and adjust the volume if desired + # db: + # volumes: + # - /path/to/volume:/var/lib/postgresql/data + + swaggerui: + # uncomment the line below for Apple M1 + # platform: linux/amd64 + ports: + - '3001:8080' diff --git a/node-cli/assets/express/example-app/docker-compose.yml b/node-cli/assets/express/example-app/docker-compose.yml new file mode 100644 index 00000000..b2810938 --- /dev/null +++ b/node-cli/assets/express/example-app/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3' + +services: + + db: + image: postgres:13-alpine + restart: always + ports: + - '${PGPORT}:5432' + environment: + POSTGRES_USER: ${PGUSER} + POSTGRES_DB: ${PGDATABASE} + POSTGRES_PASSWORD: ${PGPASSWORD} + + swaggerui: + image: swaggerapi/swagger-ui + restart: always + ports: + - '3001:8080' + volumes: + - .openapi:/openapi + environment: + SWAGGER_JSON: /openapi/openapi.json + profiles: + - tools diff --git a/node-cli/assets/express/example-app/jest.config.js b/node-cli/assets/express/example-app/jest.config.js new file mode 100644 index 00000000..99e9336c --- /dev/null +++ b/node-cli/assets/express/example-app/jest.config.js @@ -0,0 +1,55 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + // run test via Nodejs + testEnvironment: 'node', + // needed for TypeScript + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + // root dir is the app root + rootDir: '.', + // does some setup before each test file + setupFiles: [ + '/jest.setup.ts' + ], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, + // coverage directory + coverageDirectory: '/coverage', + // coverage is collected from files under src/ + collectCoverageFrom: [ + '/src/**/*.[tj]s?(x)' + ], + // and from those files ignore + coveragePathIgnorePatterns: [ + // entry points + '/src/index.[tj]sx?$', + // extensions + '/src/extensions', + // module indexes + '/.*/index.[tj]sx?$', + // e2e tests + '(spec|test)-e2e.[tj]sx?$', + // type definitions + '/src/@types/', + // database migrations + '/src/modules/database/migrations', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, +}; diff --git a/assets/express/example-app-auth0/test/jest.setup.ts b/node-cli/assets/express/example-app/jest.setup.ts similarity index 100% rename from assets/express/example-app-auth0/test/jest.setup.ts rename to node-cli/assets/express/example-app/jest.setup.ts diff --git a/node-cli/assets/express/example-app/migrations/20230302155244_initial.ts b/node-cli/assets/express/example-app/migrations/20230302155244_initial.ts new file mode 100644 index 00000000..e4058ea4 --- /dev/null +++ b/node-cli/assets/express/example-app/migrations/20230302155244_initial.ts @@ -0,0 +1,10 @@ +import { Knex } from 'knex'; +import { createUpdateTimestampsFunctionSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.raw(createUpdateTimestampsFunctionSQL); +} + +export async function down(knex: Knex): Promise { + await knex.raw('DROP FUNCTION IF EXISTS update_timestamp() CASCADE;'); +} diff --git a/node-cli/assets/express/example-app/migrations/20230302162022_create_table_users.ts b/node-cli/assets/express/example-app/migrations/20230302162022_create_table_users.ts new file mode 100644 index 00000000..7119ccb6 --- /dev/null +++ b/node-cli/assets/express/example-app/migrations/20230302162022_create_table_users.ts @@ -0,0 +1,25 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('users', (table) => { + table.bigIncrements('id'); + table + .string('userId') + .unique({ indexName: 'unq_users_user_id' }) + .index(); + table + .string('email') + .notNullable() + .unique({ indexName: 'unq_users_email' }); + table.string('password').notNullable(); + table.timestamps(false, true, true); + }); + + await knex.raw(createUpdatedAtTriggerSQL('users')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('users')); + await knex.schema.dropTable('users'); +} diff --git a/node-cli/assets/express/example-app/migrations/20230302162023_create_table_todos.ts b/node-cli/assets/express/example-app/migrations/20230302162023_create_table_todos.ts new file mode 100644 index 00000000..930d0c21 --- /dev/null +++ b/node-cli/assets/express/example-app/migrations/20230302162023_create_table_todos.ts @@ -0,0 +1,27 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('todos', (table) => { + table.bigIncrements('id'); + table.string('name').notNullable(); + table.text('note'); + table.boolean('completed').notNullable().defaultTo(false); + table.timestamps(false, true, true); + table.string('userId'); + table + .foreign('userId', 'fk_todos_user_id') + .references('userId') + .inTable('users') + .onDelete('CASCADE'); + table.index('name', 'idx_todos_name'); + table.index('createdAt', 'idx_todos_created_at'); + }); + + await knex.raw(createUpdatedAtTriggerSQL('todos')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('todos')); + await knex.schema.dropTable('todos'); +} diff --git a/node-cli/assets/express/example-app/migrations/utils/index.ts b/node-cli/assets/express/example-app/migrations/utils/index.ts new file mode 100644 index 00000000..bbec689b --- /dev/null +++ b/node-cli/assets/express/example-app/migrations/utils/index.ts @@ -0,0 +1,23 @@ +export const createUpdateTimestampsFunctionSQL = ` +CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER +LANGUAGE plpgsql +AS +$$ +BEGIN + NEW."updatedAt" = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$; +`; + +export const createUpdatedAtTriggerSQL = (tableName: string) => ` +CREATE TRIGGER update_timestamp +BEFORE UPDATE +ON ${tableName} +FOR EACH ROW +EXECUTE PROCEDURE update_timestamp(); +`; + +export const dropUpdatedAtTriggerSQL = (tableName: string) => ` +DROP TRIGGER update_timestamp ON ${tableName}; +`; diff --git a/node-cli/assets/express/example-app/scripts/generate-openapi.ts b/node-cli/assets/express/example-app/scripts/generate-openapi.ts new file mode 100644 index 00000000..afd60432 --- /dev/null +++ b/node-cli/assets/express/example-app/scripts/generate-openapi.ts @@ -0,0 +1,42 @@ +// register extensions as the very first thing in the entry point +import '@extensions/zod/register'; + +import { writeFileSync } from 'fs'; +import { resolve } from 'path'; + +import { environmentSchema } from '@utils/environment'; +import { generateDocument } from '@utils/openapi'; +import { healthcheckRoutes } from '@api/healthchecks'; +import { authRoutes } from '@api/auth'; +import { todoRoutes } from '@api/todos'; + +const run = async () => { + const env = Object.freeze(environmentSchema.parse(process.env)); + + const routes = [ + ...healthcheckRoutes, + ...authRoutes, + ...todoRoutes, + ]; + + const document = generateDocument({ + version: '3.0.3', + info: { + version: '1.0.0', + title: 'To-Do', + description: 'A To-Do application API', + }, + routes, + }); + + document.servers = [{ url: `http://localhost:${env.PORT}` }]; + + const file = resolve(__dirname, '..', '.openapi', 'openapi.json'); + const data = JSON.stringify(document, null, 2); + + writeFileSync(file, data); + + return file; +}; + +run().then(console.log).catch(console.error); diff --git a/node-cli/assets/express/example-app/src/@types/express/index.d.ts b/node-cli/assets/express/example-app/src/@types/express/index.d.ts new file mode 100644 index 00000000..cea01a71 --- /dev/null +++ b/node-cli/assets/express/example-app/src/@types/express/index.d.ts @@ -0,0 +1,15 @@ +import { AuthService } from "@api/auth"; +import { TodosService } from "@api/todos"; + +declare module 'express-serve-static-core' { + interface Request { + auth: { + sub: string; + email: string; + }; + services: { + authService: AuthService; + todosService: TodosService; + }; + } +} diff --git a/node-cli/assets/express/example-app/src/@types/knex/index.d.ts b/node-cli/assets/express/example-app/src/@types/knex/index.d.ts new file mode 100644 index 00000000..1b277163 --- /dev/null +++ b/node-cli/assets/express/example-app/src/@types/knex/index.d.ts @@ -0,0 +1,12 @@ +import { Knex } from 'knex'; +import { InsertUser, UpdateUser, User } from '@api/users/entities'; +import { InsertTodo, UpdateTodo, Todo } from '@api/todos/entities'; + +// This is how knex adds TypeScript support. +// For more information see the docs https://knexjs.org/guide/#typescript. +declare module 'knex/types/tables' { + interface Tables { + users: Knex.CompositeTableType; + todos: Knex.CompositeTableType; + } +} diff --git a/node-cli/assets/express/example-app/src/@types/query-types/index.d.ts b/node-cli/assets/express/example-app/src/@types/query-types/index.d.ts new file mode 100644 index 00000000..6ebc30c8 --- /dev/null +++ b/node-cli/assets/express/example-app/src/@types/query-types/index.d.ts @@ -0,0 +1,5 @@ +declare module 'query-types' { + import { RequestHandler } from 'express'; + + function middleware(): RequestHandler; +} diff --git a/node-cli/assets/express/example-app/src/api/auth/entities/credentials.entity.ts b/node-cli/assets/express/example-app/src/api/auth/entities/credentials.entity.ts new file mode 100644 index 00000000..9c907fa0 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/entities/credentials.entity.ts @@ -0,0 +1,4 @@ +export interface Credentials { + email: string; + password: string; +} diff --git a/node-cli/assets/express/example-app/src/api/auth/entities/index.ts b/node-cli/assets/express/example-app/src/api/auth/entities/index.ts new file mode 100644 index 00000000..7e0f4d80 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/entities/index.ts @@ -0,0 +1,3 @@ +export * from './credentials.entity'; +export * from './jwt-claims.entity'; +export * from './jwt-tokens.entity'; diff --git a/node-cli/assets/express/example-app/src/api/auth/entities/jwt-claims.entity.ts b/node-cli/assets/express/example-app/src/api/auth/entities/jwt-claims.entity.ts new file mode 100644 index 00000000..a1057ee1 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/entities/jwt-claims.entity.ts @@ -0,0 +1,4 @@ +export interface JwtClaims { + sub: string; + email: string; +} diff --git a/node-cli/assets/express/example-app/src/api/auth/entities/jwt-tokens.entity.ts b/node-cli/assets/express/example-app/src/api/auth/entities/jwt-tokens.entity.ts new file mode 100644 index 00000000..c3b09658 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/entities/jwt-tokens.entity.ts @@ -0,0 +1,3 @@ +export interface JwtTokens { + idToken: string; +} diff --git a/node-cli/assets/express/example-app/src/api/auth/index.ts b/node-cli/assets/express/example-app/src/api/auth/index.ts new file mode 100644 index 00000000..aabc4957 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/index.ts @@ -0,0 +1,4 @@ +export * from './entities'; +export * from './routes'; +export * from './schemas'; +export * from './services'; diff --git a/node-cli/assets/express/example-app/src/api/auth/routes/index.ts b/node-cli/assets/express/example-app/src/api/auth/routes/index.ts new file mode 100644 index 00000000..cd96ebd8 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/routes/index.ts @@ -0,0 +1,5 @@ +import { prefixRoutes } from '@utils/api'; +import { loginRoute } from './login.route'; +import { registerRoute } from './register.route'; + +export const authRoutes = prefixRoutes('/auth', [loginRoute, registerRoute]); diff --git a/node-cli/assets/express/example-app/src/api/auth/routes/login.route.test.ts b/node-cli/assets/express/example-app/src/api/auth/routes/login.route.test.ts new file mode 100644 index 00000000..56751f38 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/routes/login.route.test.ts @@ -0,0 +1,70 @@ +import createHttpError from 'http-errors'; +import { loginRoute as route } from './login.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const tokens = { idToken: 'token' }; + const authService = { login: jest.fn(() => tokens) }; + const req = { + body: { key: 'value' }, + services: { authService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call AuthService#login', () => { + route.handler(req as never, res as never, jest.fn()); + expect(authService.login).toHaveBeenCalledWith(req.body); + }); + + describe('when login is successful', () => { + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + beforeAll(() => { + route.handler(req as never, res as never, jest.fn()); + }); + + it('should respond with 200', () => { + expect(status).toHaveBeenCalledWith(200); + }); + + it('should send the login response', () => { + expect(send).toHaveBeenCalledWith(tokens); + }); + }); + + describe('when login failed', () => { + const authService = { login: jest.fn(() => undefined) }; + const req = { + body: { key: 'value' }, + services: { authService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + const next = jest.fn(); + + beforeAll(async () => { + await route.handler(req as never, res as never, next); + }); + + it('should throw an error', () => { + expect(next).toHaveBeenCalledWith( + expect.any(createHttpError.UnprocessableEntity) + ); + }); + + it('should not respond', () => { + expect(status).not.toHaveBeenCalled(); + expect(send).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/auth/routes/login.route.ts b/node-cli/assets/express/example-app/src/api/auth/routes/login.route.ts new file mode 100644 index 00000000..0d1345b2 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/routes/login.route.ts @@ -0,0 +1,31 @@ +import createHttpError from 'http-errors'; +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { credentialsSchema, jwtTokensSchema } from '../schemas'; + +export const loginRoute = defineRoute({ + operationId: 'login', + summary: 'Login a user', + description: 'Authenticate a user', + tags: ['Auth'], + method: 'post', + path: '/login', + request: { + body: credentialsSchema, + }, + responses: { + 200: jwtTokensSchema, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ body, services }, res) => { + const tokens = await services.authService.login(body); + + if (!tokens) { + throw new createHttpError.UnprocessableEntity( + 'Invalid email or password' + ); + } + + res.status(200).send(tokens); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/auth/routes/register.route.test.ts b/node-cli/assets/express/example-app/src/api/auth/routes/register.route.test.ts new file mode 100644 index 00000000..d8bed2aa --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/routes/register.route.test.ts @@ -0,0 +1,32 @@ +import { registerRoute as route } from './register.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const tokens = { idToken: 'token' }; + const authService = { register: jest.fn(() => tokens) }; + const req = { + body: { key: 'value' }, + services: { authService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call AuthService#register', () => { + route.handler(req as never, res as never, jest.fn()); + expect(authService.register).toHaveBeenCalledWith(req.body); + }); + + it('should respond with 200', () => { + expect(status).toHaveBeenCalledWith(200); + }); + + it('should send the register response', () => { + expect(send).toHaveBeenCalledWith(tokens); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/auth/routes/register.route.ts b/node-cli/assets/express/example-app/src/api/auth/routes/register.route.ts new file mode 100644 index 00000000..7497da79 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/routes/register.route.ts @@ -0,0 +1,24 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { credentialsSchema, jwtTokensSchema } from '../schemas'; + +export const registerRoute = defineRoute({ + operationId: 'register', + summary: 'Register a user', + description: 'Register a user', + tags: ['Auth'], + method: 'post', + path: '/register', + request: { + body: credentialsSchema, + }, + responses: { + 200: jwtTokensSchema, + 409: response.Conflict, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ body, services }, res) => { + const tokens = await services.authService.register(body); + res.status(200).send(tokens); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/auth/schemas/credentials.schema.ts b/node-cli/assets/express/example-app/src/api/auth/schemas/credentials.schema.ts new file mode 100644 index 00000000..d3581270 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/schemas/credentials.schema.ts @@ -0,0 +1,9 @@ +import { z } from 'zod'; +import { email, password } from '@utils/validation'; + +export const credentialsSchema = z + .object({ + email: email, + password: password, + }) + .openapi({ ref: 'Credentials' }); diff --git a/node-cli/assets/express/example-app/src/api/auth/schemas/index.ts b/node-cli/assets/express/example-app/src/api/auth/schemas/index.ts new file mode 100644 index 00000000..9b49534e --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/schemas/index.ts @@ -0,0 +1,2 @@ +export * from './credentials.schema'; +export * from './jwt-tokens.schema'; diff --git a/node-cli/assets/express/example-app/src/api/auth/schemas/jwt-tokens.schema.ts b/node-cli/assets/express/example-app/src/api/auth/schemas/jwt-tokens.schema.ts new file mode 100644 index 00000000..882591f0 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/schemas/jwt-tokens.schema.ts @@ -0,0 +1,11 @@ +import { z } from 'zod'; + +/** + * Possible tokens: id_token, access_token, refresh_token + * see: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims + */ +export const jwtTokensSchema = z + .object({ + idToken: z.string(), + }) + .openapi({ ref: 'JwtTokens' }); diff --git a/node-cli/assets/express/example-app/src/api/auth/services/auth.service.test.ts b/node-cli/assets/express/example-app/src/api/auth/services/auth.service.test.ts new file mode 100644 index 00000000..a371b9cb --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/auth.service.test.ts @@ -0,0 +1,102 @@ +import { DuplicateRecordError } from '@database/errors'; +import { User, UsersRepository } from '@api/users'; +import { JwtTokens } from '../entities'; +import { JwtService } from './jwt.service'; +import { PasswordService } from './password.service'; +import { AuthService } from './auth.service'; + +jest.mock('../../users/repositories/users.repository'); + +describe('AuthService', () => { + const jwt = new JwtService({ + JWT_SECRET: 'very-secret', + JWT_EXPIRATION: '1d', + }); + const passwords = new PasswordService(); + const users = new UsersRepository({} as never); + const auth = new AuthService(users, jwt, passwords); + + describe('register', () => { + describe('when the email is not regisered', () => { + const creds = { email: 'new-email@example.com', password: 'very-secret' }; + let result: JwtTokens; + let user: User | undefined; + + beforeAll(async () => { + result = await auth.register(creds); + user = await users.findByEmail(creds.email); + }); + + it('should register the email', async () => { + expect(user).toBeDefined(); + }); + + it('should hash the user password', async () => { + expect(user?.password).not.toBe(creds.password); + }); + + it('should return jwt tokens', () => { + expect(result).toEqual( + expect.objectContaining({ idToken: expect.any(String) }) + ); + }); + }); + + describe('when the email is already registered', () => { + const creds = { + email: 'registered-email@example.com', + password: 'very-secret', + }; + + beforeAll(async () => { + await auth.register(creds); + }); + + it('should throw an error', async () => { + await expect(auth.register(creds)).rejects.toThrow( + DuplicateRecordError + ); + }); + }); + }); + + describe('login', () => { + describe('when the email is not registered', () => { + it('should return undefined', async () => { + expect( + await auth.login({ + email: 'notregistered@email.com', + password: '123', + }) + ).toBeUndefined(); + }); + }); + + describe('when the email is registered', () => { + const creds = { email: 'registered@email.com', password: 'very-secret' }; + + beforeAll(async () => { + await auth.register(creds); + }); + + describe('and the password does not match', () => { + it('should return undefined', async () => { + expect( + await auth.login({ + email: creds.email, + password: 'not-my-password', + }) + ).toBeUndefined(); + }); + }); + + describe('and the email and password are valid', () => { + it('should return jwt tokens', async () => { + expect(await auth.login(creds)).toEqual( + expect.objectContaining({ idToken: expect.any(String) }) + ); + }); + }); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/auth/services/auth.service.ts b/node-cli/assets/express/example-app/src/api/auth/services/auth.service.ts new file mode 100644 index 00000000..5bd47640 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/auth.service.ts @@ -0,0 +1,48 @@ +import { UsersRepository } from '@api/users'; +import { Credentials, JwtTokens } from '../entities'; +import { PasswordService } from './password.service'; +import { JwtService } from './jwt.service'; +import { createId } from '@paralleldrive/cuid2'; +export class AuthService { + constructor( + private readonly users: UsersRepository, + private readonly jwt: JwtService, + private readonly password: PasswordService + ) {} + + async register({ email, password }: Credentials): Promise { + const user = await this.users.insertOne({ + email, + password: await this.password.hash(password), + userId: createId(), + }); + + return { + idToken: this.jwt.sign({ sub: user.id.toString(), email }), + }; + } + + async login({ + email, + password, + }: Credentials): Promise { + const user = await this.users.findByEmail(email); + + if (!user) { + return; + } + + const passwordMatches = await this.password.compare( + password, + user.password + ); + + if (!passwordMatches) { + return; + } + + return { + idToken: this.jwt.sign({ sub: user.userId.toString(), email }), + }; + } +} diff --git a/node-cli/assets/express/example-app/src/api/auth/services/index.ts b/node-cli/assets/express/example-app/src/api/auth/services/index.ts new file mode 100644 index 00000000..a3c1b63b --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/index.ts @@ -0,0 +1,3 @@ +export * from './auth.service'; +export * from './jwt.service'; +export * from './password.service'; diff --git a/node-cli/assets/express/example-app/src/api/auth/services/jwt.service.test.ts b/node-cli/assets/express/example-app/src/api/auth/services/jwt.service.test.ts new file mode 100644 index 00000000..9cd98751 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/jwt.service.test.ts @@ -0,0 +1,20 @@ +import { verify } from 'jsonwebtoken'; +import { JwtService } from './jwt.service'; + +describe('JwtService', () => { + const env = { + JWT_SECRET: 'very-secret', + JWT_EXPIRATION: '1d', + }; + + const jwt = new JwtService(env); + + describe('sign', () => { + it('should sign the claims returning a JWT token', () => { + const claims = { sub: '1', email: 'email@example.com' }; + const token = jwt.sign(claims); + const payload = verify(token, env.JWT_SECRET); + expect(payload).toEqual(expect.objectContaining(claims)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/auth/services/jwt.service.ts b/node-cli/assets/express/example-app/src/api/auth/services/jwt.service.ts new file mode 100644 index 00000000..5de7c927 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/jwt.service.ts @@ -0,0 +1,15 @@ +import jwt from 'jsonwebtoken'; +import { Environment } from '@utils/environment'; +import { JwtClaims } from '../entities'; + +export class JwtService { + constructor( + private readonly env: Pick + ) {} + + sign(claims: JwtClaims): string { + return jwt.sign(claims, this.env.JWT_SECRET, { + expiresIn: this.env.JWT_EXPIRATION, + }); + } +} diff --git a/node-cli/assets/express/example-app/src/api/auth/services/password.service.test.ts b/node-cli/assets/express/example-app/src/api/auth/services/password.service.test.ts new file mode 100644 index 00000000..aad891d5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/password.service.test.ts @@ -0,0 +1,27 @@ +import { PasswordService } from './password.service'; + +describe('PasswordService', () => { + const passwords = new PasswordService(); + + describe('hash', () => { + it('should return a different string', async () => { + expect(await passwords.hash('some-text')).not.toBe('some-text'); + }); + }); + + describe('compare', () => { + it('should be true when the hash is of the value', async () => { + const password = 'very-secret'; + const hash = await passwords.hash(password); + + expect(await passwords.compare(password, hash)).toBe(true); + }); + + it('should be false when the hash is not of the value', async () => { + const password = 'very-secret'; + const hash = await passwords.hash(password); + + expect(await passwords.compare('not-my-password', hash)).toBe(false); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/auth/services/password.service.ts b/node-cli/assets/express/example-app/src/api/auth/services/password.service.ts new file mode 100644 index 00000000..0c931e87 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/auth/services/password.service.ts @@ -0,0 +1,11 @@ +import bcrypt from 'bcrypt'; + +export class PasswordService { + async hash(password: string): Promise { + return bcrypt.hash(password, await bcrypt.genSalt()); + } + + async compare(password: string, hash: string): Promise { + return bcrypt.compare(password, hash); + } +} diff --git a/node-cli/assets/express/example-app/src/api/healthchecks/index.ts b/node-cli/assets/express/example-app/src/api/healthchecks/index.ts new file mode 100644 index 00000000..a3820983 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/healthchecks/index.ts @@ -0,0 +1 @@ +export * from './routes'; diff --git a/node-cli/assets/express/example-app/src/api/healthchecks/routes/index.ts b/node-cli/assets/express/example-app/src/api/healthchecks/routes/index.ts new file mode 100644 index 00000000..063c5cb1 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/healthchecks/routes/index.ts @@ -0,0 +1,8 @@ +import { prefixRoutes } from '@utils/api'; +import { livenessRoute } from './live.route'; +import { readinessRoute } from './ready.route'; + +export const healthcheckRoutes = prefixRoutes('/healthz', [ + livenessRoute, + readinessRoute, +]); diff --git a/node-cli/assets/express/example-app/src/api/healthchecks/routes/live.route.test.ts b/node-cli/assets/express/example-app/src/api/healthchecks/routes/live.route.test.ts new file mode 100644 index 00000000..1f66d086 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/healthchecks/routes/live.route.test.ts @@ -0,0 +1,15 @@ +import { livenessRoute as route } from './live.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + it('should respond with "OK"', () => { + const res = { send: jest.fn() }; + route.handler({} as never, res as never, jest.fn()); + expect(res.send).toHaveBeenCalledWith('OK'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/healthchecks/routes/live.route.ts b/node-cli/assets/express/example-app/src/api/healthchecks/routes/live.route.ts new file mode 100644 index 00000000..d20d6104 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/healthchecks/routes/live.route.ts @@ -0,0 +1,26 @@ +import { RouteDefinition } from '@utils/api'; + +export const livenessRoute: RouteDefinition = { + operationId: 'health-liveness', + summary: 'Liveness endpoint', + description: 'Used to check whether the application is alive.', + tags: ['Healthchecks'], + method: 'get', + path: '/live', + handler: function (_req, res) { + res.send('OK'); + }, + responses: { + 200: { + description: 'Application is alive', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }, + }, +}; diff --git a/node-cli/assets/express/example-app/src/api/healthchecks/routes/ready.route.test.ts b/node-cli/assets/express/example-app/src/api/healthchecks/routes/ready.route.test.ts new file mode 100644 index 00000000..4a751b8f --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/healthchecks/routes/ready.route.test.ts @@ -0,0 +1,15 @@ +import { readinessRoute as route } from './ready.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + it('should respond with "OK"', () => { + const res = { send: jest.fn() }; + route.handler({} as never, res as never, jest.fn()); + expect(res.send).toHaveBeenCalledWith('OK'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/healthchecks/routes/ready.route.ts b/node-cli/assets/express/example-app/src/api/healthchecks/routes/ready.route.ts new file mode 100644 index 00000000..a6d4ef7c --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/healthchecks/routes/ready.route.ts @@ -0,0 +1,28 @@ +import { RouteDefinition } from '@utils/api'; + +export const readinessRoute: RouteDefinition = { + operationId: 'health-readiness', + summary: 'Readiness endpoint', + description: + 'Used to check whether the application is ready to receive requests.', + tags: ['Healthchecks'], + method: 'get', + path: '/ready', + handler: function (_req, res) { + // Add your specific checks + res.send('OK'); + }, + responses: { + 200: { + description: 'Application is ready', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }, + }, +}; diff --git a/node-cli/assets/express/example-app/src/api/todos/entities/index.ts b/node-cli/assets/express/example-app/src/api/todos/entities/index.ts new file mode 100644 index 00000000..1382a966 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/entities/index.ts @@ -0,0 +1,4 @@ +export * from './insert-todo.entity'; +export * from './list-todos-query.entity'; +export * from './todo.entity'; +export * from './update-todo.entity'; diff --git a/node-cli/assets/express/example-app/src/api/todos/entities/insert-todo.entity.ts b/node-cli/assets/express/example-app/src/api/todos/entities/insert-todo.entity.ts new file mode 100644 index 00000000..d7c00eca --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/entities/insert-todo.entity.ts @@ -0,0 +1,6 @@ +import { Insert } from '@database/operations'; +import { Todo } from './todo.entity'; + +export type InsertTodo = Insert< + Pick +>; diff --git a/node-cli/assets/express/example-app/src/api/todos/entities/list-todos-query.entity.ts b/node-cli/assets/express/example-app/src/api/todos/entities/list-todos-query.entity.ts new file mode 100644 index 00000000..f1285315 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/entities/list-todos-query.entity.ts @@ -0,0 +1,13 @@ +import { Pagination } from '@utils/query'; +import { Todo } from './todo.entity'; + +export interface ListTodosQuery { + filters?: Partial>; + sorts?: + | { + column: 'name' | 'createdAt'; + order?: 'asc' | 'desc' | undefined; + }[] + | undefined; + pagination?: Pagination; +} diff --git a/node-cli/assets/express/example-app/src/api/todos/entities/todo.entity.ts b/node-cli/assets/express/example-app/src/api/todos/entities/todo.entity.ts new file mode 100644 index 00000000..c8fce6bc --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/entities/todo.entity.ts @@ -0,0 +1,9 @@ +export interface Todo { + id: number; + userId: string; + name: string; + note: string | null; + completed: boolean; + createdAt: string; + updatedAt: string; +} diff --git a/node-cli/assets/express/example-app/src/api/todos/entities/update-todo.entity.ts b/node-cli/assets/express/example-app/src/api/todos/entities/update-todo.entity.ts new file mode 100644 index 00000000..b94d8a94 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/entities/update-todo.entity.ts @@ -0,0 +1,4 @@ +import { Update } from '@database/operations'; +import { Todo } from './todo.entity'; + +export type UpdateTodo = Update>; diff --git a/node-cli/assets/express/example-app/src/api/todos/error-mappings/index.ts b/node-cli/assets/express/example-app/src/api/todos/error-mappings/index.ts new file mode 100644 index 00000000..cec33482 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './todo-user-not-found.error-mapping'; diff --git a/node-cli/assets/express/example-app/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/node-cli/assets/express/example-app/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts new file mode 100644 index 00000000..02f8ead3 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts @@ -0,0 +1,6 @@ +import { foreignKeyViolation } from '@database/error-mappings'; + +export const TodoUserNotFound = foreignKeyViolation( + 'fk_todos_user_id', + 'User not found' +); diff --git a/node-cli/assets/express/example-app/src/api/todos/filters/index.ts b/node-cli/assets/express/example-app/src/api/todos/filters/index.ts new file mode 100644 index 00000000..25a6d221 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/filters/index.ts @@ -0,0 +1,2 @@ +export * from './todos-by-completed.filter'; +export * from './todos-by-name.filter'; diff --git a/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.test.ts b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.test.ts new file mode 100644 index 00000000..1706a438 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.test.ts @@ -0,0 +1,15 @@ +import { filterByCompleted } from './todos-by-completed.filter'; + +const queryBuilder = { + whereILike: jest.fn(), + where: jest.fn(), +}; + +describe('filterByCompleted', () => { + const completed = true; + + it('should filter by completed', () => { + filterByCompleted(queryBuilder as never, completed); + expect(queryBuilder.where).toHaveBeenCalledWith({ completed }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.ts b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.ts new file mode 100644 index 00000000..29f9eddc --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-completed.filter.ts @@ -0,0 +1,7 @@ +import { Tables } from 'knex/types/tables'; +import { Filter } from '@utils/query'; + +export const filterByCompleted: Filter = ( + qb, + completed +) => qb.where({ completed }); diff --git a/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.test.ts b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.test.ts new file mode 100644 index 00000000..af71252e --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.test.ts @@ -0,0 +1,15 @@ +import { filterByName } from './todos-by-name.filter'; + +const queryBuilder = { + whereILike: jest.fn(), + where: jest.fn(), +}; + +describe('filterByName', () => { + const name = 'Jonh'; + + it('should filter by name', () => { + filterByName(queryBuilder as never, name); + expect(queryBuilder.whereILike).toHaveBeenCalledWith('name', `%${name}%`); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.ts b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.ts new file mode 100644 index 00000000..be32c21c --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/filters/todos-by-name.filter.ts @@ -0,0 +1,5 @@ +import { Tables } from 'knex/types/tables'; +import { Filter } from '@utils/query'; + +export const filterByName: Filter = (qb, name) => + qb.whereILike('name', `%${name}%`); diff --git a/node-cli/assets/express/example-app/src/api/todos/index.ts b/node-cli/assets/express/example-app/src/api/todos/index.ts new file mode 100644 index 00000000..5833c69d --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/index.ts @@ -0,0 +1,8 @@ +export * from './entities'; +export * from './error-mappings'; +export * from './filters'; +export * from './repositories'; +export * from './routes'; +export * from './schemas'; +export * from './services'; +export * from './sorters'; diff --git a/node-cli/assets/express/example-app/src/api/todos/repositories/__mocks__/todos.repository.ts b/node-cli/assets/express/example-app/src/api/todos/repositories/__mocks__/todos.repository.ts new file mode 100644 index 00000000..23f212d5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/repositories/__mocks__/todos.repository.ts @@ -0,0 +1,82 @@ +import { Paginated, extractPagination } from '@utils/query'; +import { InsertTodo, ListTodosQuery, Todo, UpdateTodo } from '../../entities'; + +export class TodosRepository { + private lastId = 0; + private records: Todo[] = []; + + private nextId() { + return ++this.lastId; + } + + async insertOne(input: InsertTodo): Promise { + const record = { + id: this.nextId(), + ...input, + note: input.note ?? null, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + this.records.push(record); + + return record; + } + + async findById( + id: Todo['id'], + userId: Todo['userId'] + ): Promise { + return this.records.find((r) => r.id === id && r.userId === userId); + } + + async updateById( + id: Todo['id'], + userId: Todo['userId'], + input: UpdateTodo + ): Promise { + const record = await this.findById(id, userId); + + if (!record) { + return; + } + + Object.assign(record, input); + + return record; + } + + async deleteById(id: Todo['id'], userId: Todo['userId']): Promise { + const record = await this.findById(id, userId); + + if (!record) { + return 0; + } + + this.records.splice(this.records.indexOf(record), 1); + + return 1; + } + + async list( + userId: Todo['userId'], + query: ListTodosQuery + ): Promise> { + const { page, items } = extractPagination(query.pagination); + + const records = this.records.filter((r) => r.userId === userId); + + const start = (page - 1) * items; + const end = start + items; + const data = records.slice(start, end); + + return { + data, + meta: { + total: records.length, + page, + items, + }, + }; + } +} diff --git a/node-cli/assets/express/example-app/src/api/todos/repositories/index.ts b/node-cli/assets/express/example-app/src/api/todos/repositories/index.ts new file mode 100644 index 00000000..2639d438 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/repositories/index.ts @@ -0,0 +1 @@ +export * from './todos.repository'; diff --git a/node-cli/assets/express/example-app/src/api/todos/repositories/todos.repository.test.ts b/node-cli/assets/express/example-app/src/api/todos/repositories/todos.repository.test.ts new file mode 100644 index 00000000..bdcd924c --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/repositories/todos.repository.test.ts @@ -0,0 +1,198 @@ +import Knex from 'knex'; +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { RecordNotFoundError } from '@database/errors'; +import { Todo } from '../entities'; +import { TodosRepository } from './todos.repository'; +import { filterByCompleted, filterByName } from '../filters'; +import { sortByCreatedAt, sortByName } from '../sorters'; + +describe('TodosRepository', () => { + const knex = Knex({ client: 'pg', connection: {} }); + const todos = new TodosRepository(knex); + const todosQb = knex('todos'); + + describe('insertOne', () => { + it('should return the newly created record', async () => { + const todo = { + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + }; + + jest.spyOn(todosQb, 'insert'); + jest.spyOn(todosQb, 'returning'); + jest.spyOn(todosQb, 'then'); + jest.spyOn(todosQb, 'catch').mockImplementationOnce(async () => ({ + id: 1, + ...todo, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })); + + const result = await todos.insertOne(todo); + + expect(todosQb.insert).toHaveBeenCalledWith(todo); + expect(todosQb.returning).toHaveBeenCalledWith('*'); + expect(todosQb.then).toHaveBeenCalled(); + expect(todosQb.catch).toHaveBeenCalled(); + + expect(result).toEqual(expect.objectContaining(todo)); + }); + }); + + describe('findById', () => { + it('should retrun the first record found', async () => { + const todo: Todo = { + id: 1, + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + jest.spyOn(todosQb, 'where'); + jest + .spyOn(todosQb, 'first') + .mockImplementationOnce(() => Promise.resolve(todo) as never); + + const result = await todos.findById(todo.id, todo.userId); + + expect(todosQb.where).toHaveBeenCalledWith({ + id: todo.id, + userId: todo.userId, + }); + expect(todosQb.first).toHaveBeenCalled(); + + expect(result).toEqual(todo); + }); + }); + + describe('updateById', () => { + it('should perform a findById with empty payload', async () => { + const todo: Todo = { + id: 1, + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + jest.spyOn(todos, 'findById').mockImplementationOnce(async () => todo); + jest.spyOn(todosQb, 'update'); + + await todos.updateById(todo.id, todo.userId, {}); + + expect(todos.findById).toHaveBeenCalledWith(todo.id, todo.userId); + expect(todosQb.update).not.toHaveBeenCalled(); + }); + + it('should update the record and return it', async () => { + const todo: Todo = { + id: 1, + userId: '1', + name: 'Laundry', + note: 'Now!', + completed: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + const input = { note: 'updated' }; + const updated = { ...todo, ...input }; + + jest.spyOn(todosQb, 'where'); + jest.spyOn(todosQb, 'update'); + jest.spyOn(todosQb, 'returning'); + jest.spyOn(todosQb, 'then'); + jest + .spyOn(todosQb, 'catch') + .mockImplementationOnce(() => Promise.resolve(updated) as never); + + const result = await todos.updateById(todo.id, todo.userId, input); + + expect(todosQb.where).toHaveBeenCalledWith({ + id: todo.id, + userId: todo.userId, + }); + expect(todosQb.update).toHaveBeenCalledWith(input); + expect(todosQb.returning).toHaveBeenCalledWith('*'); + expect(todosQb.then).toHaveBeenCalled(); + expect(todosQb.catch).toHaveBeenCalled(); + + expect(result).toEqual(updated); + }); + + it('should rethrow fk violation on userId as RecordNotFound', () => { + const error = new DatabaseError('fk violation', 72, 'error'); + error.code = PostgresError.FOREIGN_KEY_VIOLATION; + error.constraint = 'fk_todos_user_id'; + const thenable = () => ({ then: () => Promise.reject(error) }); + + jest + .spyOn(todosQb, 'returning') + .mockImplementationOnce(thenable as never); + + expect( + todos.insertOne({ + userId: '1', + name: 'name', + note: 'note', + completed: false, + }) + ).rejects.toThrow(new RecordNotFoundError('User not found')); + }); + }); + + describe('deleteById', () => { + it('should delete the record by ID', async () => { + jest.spyOn(todosQb, 'where'); + jest + .spyOn(todosQb, 'del') + .mockImplementationOnce(() => Promise.resolve(1) as never); + + const result = await todos.deleteById(1, '1'); + + expect(todosQb.where).toHaveBeenCalledWith({ id: 1, userId: '1' }); + expect(todosQb.del).toHaveBeenCalled(); + expect(result).toBe(1); + }); + }); + + describe('list', () => { + it('should list the records', async () => { + jest.spyOn(todosQb, 'where'); + jest.spyOn(todosQb, 'clone'); + jest.spyOn(todosQb, 'filter'); + jest.spyOn(todosQb, 'sort'); + jest + .spyOn(todosQb, 'paginate') + .mockImplementationOnce(() => Promise.resolve([]) as never); + jest + .spyOn(todosQb, 'count') + .mockImplementationOnce(() => Promise.resolve([{ count: 0 }]) as never); + + const userId = '1'; + const query = {}; + + await todos.list(userId, query); + + expect(todosQb.where).toHaveBeenCalledWith({ userId }); + expect(todosQb.clone).toHaveBeenCalledTimes(2); + expect(todosQb.filter).toHaveBeenCalledWith(undefined, { + name: filterByName, + completed: filterByCompleted, + }); + expect(todosQb.sort).toHaveBeenCalledWith(undefined, { + name: sortByName, + createdAt: sortByCreatedAt, + }); + expect(todosQb.count).toHaveBeenCalled(); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/repositories/todos.repository.ts b/node-cli/assets/express/example-app/src/api/todos/repositories/todos.repository.ts new file mode 100644 index 00000000..2308862c --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/repositories/todos.repository.ts @@ -0,0 +1,83 @@ +import { Knex } from 'knex'; +import { rethrowError } from '@utils/error'; +import { Paginated, extractPagination, parseCount } from '@utils/query'; +import { InsertTodo, ListTodosQuery, Todo, UpdateTodo } from '../entities'; +import { TodoUserNotFound } from '../error-mappings'; +import { filterByCompleted, filterByName } from '../filters'; +import { sortByCreatedAt, sortByName } from '../sorters'; + +export class TodosRepository { + constructor(private readonly knex: Knex) {} + + async insertOne(input: InsertTodo): Promise { + return await this.knex('todos') + .insert(input) + .returning('*') + .then(([todo]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async findById( + id: Todo['id'], + userId: Todo['userId'] + ): Promise { + return await this.knex('todos').where({ id, userId }).first(); + } + + async updateById( + id: Todo['id'], + userId: Todo['userId'], + input: UpdateTodo + ): Promise { + if (Object.keys(input).length === 0) { + return this.findById(id, userId); + } + + return await this.knex('todos') + .where({ id, userId }) + .update(input) + .returning('*') + .then(([todo]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async deleteById(id: Todo['id'], userId: Todo['userId']): Promise { + return await this.knex('todos').where({ id, userId }).del(); + } + + async list( + userId: Todo['userId'], + query: ListTodosQuery + ): Promise> { + const qb = this.knex('todos').where({ userId }); + + const data = await qb + .clone() + .filter(query.filters, { + name: filterByName, + completed: filterByCompleted, + }) + .sort(query.sorts, { + name: sortByName, + createdAt: sortByCreatedAt, + }) + .paginate(query.pagination); + + const total = await qb + .clone() + .filter(query.filters, { + name: filterByName, + completed: filterByCompleted, + }) + .count() + .then(parseCount); + + return { + data: data, + meta: { + ...extractPagination(query.pagination), + total, + }, + }; + } +} diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/create.route.test.ts b/node-cli/assets/express/example-app/src/api/todos/routes/create.route.test.ts new file mode 100644 index 00000000..b8c1d2b8 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/create.route.test.ts @@ -0,0 +1,36 @@ +import { createTodoRoute as route } from './create.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const todosService = { create: jest.fn(() => todo) }; + const req = { + auth: { sub: '1' }, + body: { name: 'Laundry' }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#create', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.create).toHaveBeenCalledWith({ + ...req.body, + userId: req.auth.sub, + }); + }); + + it('should respond with 201', () => { + expect(status).toHaveBeenCalledWith(201); + }); + + it('should send the created todo', () => { + expect(send).toHaveBeenCalledWith(todo); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/create.route.ts b/node-cli/assets/express/example-app/src/api/todos/routes/create.route.ts new file mode 100644 index 00000000..aabb811b --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/create.route.ts @@ -0,0 +1,29 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { createTodoSchema, todoSchema } from '../schemas'; + +export const createTodoRoute = defineRoute({ + operationId: 'todo-create', + summary: 'Create a To-Do', + description: 'Create a new To-Do item', + tags: ['v1', 'Todo'], + method: 'post', + path: '/', + authenticate: true, + request: { + body: createTodoSchema, + }, + responses: { + 201: todoSchema, + 401: response.Unauthorized, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ body, services, auth: { sub } }, res) => { + const todo = await services.todosService.create({ + ...body, + userId: sub, + }); + + res.status(201).send(todo); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/delete.route.test.ts b/node-cli/assets/express/example-app/src/api/todos/routes/delete.route.test.ts new file mode 100644 index 00000000..a8530dca --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/delete.route.test.ts @@ -0,0 +1,35 @@ +import { deleteTodoRoute as route } from './delete.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todosService = { delete: jest.fn(() => 1) }; + const req = { + auth: { sub: '1' }, + params: { id: 1 }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#delete', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.delete).toHaveBeenCalledWith( + req.params.id, + req.auth.sub + ); + }); + + it('should respond with 201', () => { + expect(status).toHaveBeenCalledWith(204); + }); + + it('should send nothing', () => { + expect(send).toHaveBeenCalled(); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/delete.route.ts b/node-cli/assets/express/example-app/src/api/todos/routes/delete.route.ts new file mode 100644 index 00000000..1a1479c8 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/delete.route.ts @@ -0,0 +1,27 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { todoIdParamSchema } from '../schemas'; + +export const deleteTodoRoute = defineRoute({ + operationId: 'todo-delete', + summary: 'Delete a To-Do', + description: 'Delete a To-Do item', + tags: ['v1', 'Todo'], + method: 'delete', + path: '/:id', + authenticate: true, + request: { + params: todoIdParamSchema, + }, + responses: { + 204: response.NoContent, + 401: response.Unauthorized, + 404: response.NotFound, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ params, services, auth: { sub } }, res) => { + await services.todosService.delete(params.id, sub); + + res.status(204).send(); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/get.route.test.ts b/node-cli/assets/express/example-app/src/api/todos/routes/get.route.test.ts new file mode 100644 index 00000000..4ee1e4af --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/get.route.test.ts @@ -0,0 +1,32 @@ +import { getTodoRoute as route } from './get.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const todosService = { find: jest.fn(() => todo) }; + const req = { + auth: { sub: '1' }, + params: { id: 1 }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#find', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.find).toHaveBeenCalledWith( + req.params.id, + req.auth.sub + ); + }); + + it('should send the found todo', () => { + expect(send).toHaveBeenCalledWith(todo); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/get.route.ts b/node-cli/assets/express/example-app/src/api/todos/routes/get.route.ts new file mode 100644 index 00000000..e73a94b3 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/get.route.ts @@ -0,0 +1,27 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { todoIdParamSchema, todoSchema } from '../schemas'; + +export const getTodoRoute = defineRoute({ + operationId: 'todo-get', + summary: 'Get a To-Do', + description: 'Get a To-Do item', + tags: ['v1', 'Todo'], + method: 'get', + path: '/:id', + authenticate: true, + request: { + params: todoIdParamSchema, + }, + responses: { + 200: todoSchema, + 401: response.Unauthorized, + 404: response.NotFound, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ services, params, auth: { sub } }, res) => { + const todo = await services.todosService.find(params.id, sub); + + res.send(todo); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/index.ts b/node-cli/assets/express/example-app/src/api/todos/routes/index.ts new file mode 100644 index 00000000..68cf2650 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/index.ts @@ -0,0 +1,14 @@ +import { prefixRoutes } from '@utils/api'; +import { createTodoRoute } from './create.route'; +import { deleteTodoRoute } from './delete.route'; +import { getTodoRoute } from './get.route'; +import { listTodosRoute } from './list.route'; +import { updateTodoRoute } from './update.route'; + +export const todoRoutes = prefixRoutes('/v1/todos', [ + createTodoRoute, + deleteTodoRoute, + getTodoRoute, + listTodosRoute, + updateTodoRoute, +]); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/list.route.test.ts b/node-cli/assets/express/example-app/src/api/todos/routes/list.route.test.ts new file mode 100644 index 00000000..23c02fd5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/list.route.test.ts @@ -0,0 +1,40 @@ +import { listTodosRoute as route } from './list.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const response = { + data: [todo], + meta: { + total: 1, + page: 1, + items: 20, + }, + }; + const todosService = { list: jest.fn(() => response) }; + const req = { + auth: { sub: '1' }, + query: { filters: { name: 'Laundy' } }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#list', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.list).toHaveBeenCalledWith( + req.auth.sub, + req.query + ); + }); + + it('should send the paginated todo list', () => { + expect(send).toHaveBeenCalledWith(response); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/list.route.ts b/node-cli/assets/express/example-app/src/api/todos/routes/list.route.ts new file mode 100644 index 00000000..8ed448b1 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/list.route.ts @@ -0,0 +1,26 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { paginated } from '@utils/query'; +import { todoSchema, listTodosQuerySchema } from '../schemas'; + +export const listTodosRoute = defineRoute({ + operationId: 'todo-list', + summary: 'List To-Dos', + description: 'List To-Do items', + tags: ['v1', 'Todo'], + method: 'get', + path: '/', + authenticate: true, + request: { + query: listTodosQuerySchema, + }, + responses: { + 401: response.Unauthorized, + 200: paginated(todoSchema), + }, +}).attachHandler( + asyncHandler(async ({ query, services, auth: { sub } }, res) => { + const todos = await services.todosService.list(sub, query); + + res.send(todos); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/update.route.test.ts b/node-cli/assets/express/example-app/src/api/todos/routes/update.route.test.ts new file mode 100644 index 00000000..c41849d7 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/update.route.test.ts @@ -0,0 +1,34 @@ +import { updateTodoRoute as route } from './update.route'; + +describe('route', () => { + it('should be defined', () => { + expect(route).toBeDefined(); + }); + + describe('handler', () => { + const todo = { id: 1, name: 'Laundry' }; + const todosService = { update: jest.fn(() => todo) }; + const req = { + auth: { sub: '1' }, + params: { id: 1 }, + body: { name: 'Laundry' }, + services: { todosService }, + }; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + it('should call TodoService#update', () => { + route.handler(req as never, res as never, jest.fn()); + expect(todosService.update).toHaveBeenCalledWith( + req.params.id, + req.auth.sub, + req.body + ); + }); + + it('should send the updated todo', () => { + expect(send).toHaveBeenCalledWith(todo); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/routes/update.route.ts b/node-cli/assets/express/example-app/src/api/todos/routes/update.route.ts new file mode 100644 index 00000000..4b0a19b2 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/routes/update.route.ts @@ -0,0 +1,32 @@ +import { asyncHandler, defineRoute, response } from '@utils/api'; +import { todoIdParamSchema, updateTodoSchema, todoSchema } from '../schemas'; + +export const updateTodoRoute = defineRoute({ + operationId: 'todo-update', + summary: 'Update a To-Do', + description: 'Update a To-Do item', + tags: ['v1', 'Todo'], + method: 'patch', + path: '/:id', + authenticate: true, + request: { + params: todoIdParamSchema, + body: updateTodoSchema, + }, + responses: { + 200: todoSchema, + 401: response.Unauthorized, + 404: response.NotFound, + 422: response.UnprocessableEntity, + }, +}).attachHandler( + asyncHandler(async ({ body, params, services, auth: { sub } }, res) => { + const todo = await services.todosService.update( + params.id, + sub, + body + ); + + res.send(todo); + }) +); diff --git a/node-cli/assets/express/example-app/src/api/todos/schemas/create-todo.schema.ts b/node-cli/assets/express/example-app/src/api/todos/schemas/create-todo.schema.ts new file mode 100644 index 00000000..1bc8de02 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/schemas/create-todo.schema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; +import { todoSchema } from './todo.schema'; + +export const createTodoSchema = z.object({ + name: todoSchema.shape.name, + note: todoSchema.shape.note.optional(), + completed: todoSchema.shape.completed.optional().default(false), +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/schemas/index.ts b/node-cli/assets/express/example-app/src/api/todos/schemas/index.ts new file mode 100644 index 00000000..ed4024ed --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/schemas/index.ts @@ -0,0 +1,5 @@ +export * from './create-todo.schema'; +export * from './list-todos-query.schema'; +export * from './todo-id-param.schema'; +export * from './todo.schema'; +export * from './update-todo.schema'; diff --git a/node-cli/assets/express/example-app/src/api/todos/schemas/list-todos-query.schema.ts b/node-cli/assets/express/example-app/src/api/todos/schemas/list-todos-query.schema.ts new file mode 100644 index 00000000..8ad2317a --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/schemas/list-todos-query.schema.ts @@ -0,0 +1,13 @@ +import { pagination, sorts } from '@utils/query'; +import { z } from 'zod'; + +export const listTodosQuerySchema = z.object({ + filters: z + .object({ + name: z.string().optional(), + completed: z.boolean().optional(), + }) + .optional(), + sorts: sorts(z.enum(['name', 'createdAt'])).optional(), + pagination: pagination.optional(), +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/schemas/todo-id-param.schema.ts b/node-cli/assets/express/example-app/src/api/todos/schemas/todo-id-param.schema.ts new file mode 100644 index 00000000..8d70952c --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/schemas/todo-id-param.schema.ts @@ -0,0 +1,6 @@ +import z from 'zod'; +import { todoSchema } from './todo.schema'; + +export const todoIdParamSchema = z.object({ + id: todoSchema.shape.id.coerce(), +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/schemas/todo.schema.ts b/node-cli/assets/express/example-app/src/api/todos/schemas/todo.schema.ts new file mode 100644 index 00000000..aca444b7 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/schemas/todo.schema.ts @@ -0,0 +1,20 @@ +import { z } from 'zod'; +import { withId, withTimestamps } from '@utils/validation'; +import { userSchema } from '@api/users/schemas'; + +export const todoSchema = z + .object({ + userId: userSchema.shape.id, + name: z.string().trim().min(1).max(255).openapi({ example: 'Laundry' }), + note: z + .string() + .trim() + .min(1) + .max(255) + .nullable() + .openapi({ example: 'Buy detergent' }), + completed: z.boolean().openapi({ example: false }), + }) + .merge(withId) + .merge(withTimestamps) + .openapi({ ref: 'ToDo' }); diff --git a/node-cli/assets/express/example-app/src/api/todos/schemas/update-todo.schema.ts b/node-cli/assets/express/example-app/src/api/todos/schemas/update-todo.schema.ts new file mode 100644 index 00000000..7a26911d --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/schemas/update-todo.schema.ts @@ -0,0 +1,9 @@ +import { todoSchema } from './todo.schema'; + +export const updateTodoSchema = todoSchema + .pick({ + name: true, + note: true, + completed: true, + }) + .partial(); diff --git a/node-cli/assets/express/example-app/src/api/todos/services/index.ts b/node-cli/assets/express/example-app/src/api/todos/services/index.ts new file mode 100644 index 00000000..df9135e2 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/services/index.ts @@ -0,0 +1 @@ +export * from './todos.service'; diff --git a/node-cli/assets/express/example-app/src/api/todos/services/todos.service.test.ts b/node-cli/assets/express/example-app/src/api/todos/services/todos.service.test.ts new file mode 100644 index 00000000..4b048cb6 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/services/todos.service.test.ts @@ -0,0 +1,124 @@ +import { RecordNotFoundError } from '@database/errors'; +import { Todo } from '../entities'; +import { TodosRepository } from '../repositories'; +import { TodosService } from './todos.service'; + +jest.mock('../repositories/todos.repository'); + +describe('TodoService', () => { + const todos = new TodosRepository({} as never); + const todosService = new TodosService(todos); + const userId = '1'; + + describe('create', () => { + it('should create the record', async () => { + const todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + expect(await todosService.find(todo.id, userId)).toBeDefined(); + }); + }); + + describe('find', () => { + describe('when the record does not exist', () => { + it('should throw an error', async () => { + await expect( + todosService.find(Date.now(), userId) + ).rejects.toThrow(RecordNotFoundError); + }); + }); + + describe('when the record exists', () => { + let todo: Todo; + + beforeAll(async () => { + todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + }); + + it('should return the record', async () => { + expect(await todosService.find(todo.id, userId)).toBeDefined(); + }); + }); + }); + + describe('update', () => { + describe('when the record does not exist', () => { + it('should throw an error', async () => { + await expect( + todosService.update(Date.now(), userId, { completed: true }) + ).rejects.toThrow(RecordNotFoundError); + }); + }); + + describe('when the record exists', () => { + let todo: Todo; + + beforeAll(async () => { + todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + }); + + it('should update the record', async () => { + const input = { name: 'new-name', note: 'new note', completed: true }; + const updated = await todosService.update(todo.id, userId, input); + expect(updated).toEqual(expect.objectContaining(input)); + }); + }); + }); + + describe('delete', () => { + describe('when the record does not exist', () => { + it('should throw an error', async () => { + await expect( + todosService.delete(Date.now(), userId) + ).rejects.toThrow(RecordNotFoundError); + }); + }); + + describe('when the record exists', () => { + let todo: Todo; + + beforeAll(async () => { + todo = await todosService.create({ + userId, + name: 'Todo', + completed: false, + }); + }); + + it('should delete the record', async () => { + await todosService.delete(todo.id, userId); + await expect(todosService.find(todo.id, userId)).rejects.toThrow( + RecordNotFoundError + ); + }); + }); + }); + + describe('list', () => { + const userId = '2'; + + beforeAll(async () => { + await todosService.create({ userId, name: 'Abc 1', completed: false }); + await todosService.create({ userId, name: 'Abc 2', completed: true }); + await todosService.create({ userId, name: 'Zyx 1', completed: false }); + await todosService.create({ userId, name: 'Zyx 2', completed: true }); + await todosService.create({ userId, name: 'Zyx 3', completed: false }); + }); + + it('should list my toods', async () => { + const result = await todosService.list(userId, {}); + expect(result.data).toHaveLength(5); + expect(result.meta.total).toBe(5); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/services/todos.service.ts b/node-cli/assets/express/example-app/src/api/todos/services/todos.service.ts new file mode 100644 index 00000000..7582b7f2 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/services/todos.service.ts @@ -0,0 +1,40 @@ +import { Paginated, definedOrNotFound, updatedOrNotFound } from '@utils/query'; +import { InsertTodo, ListTodosQuery, Todo, UpdateTodo } from '../entities'; +import { TodosRepository } from '../repositories'; + +export class TodosService { + constructor(private readonly todos: TodosRepository) {} + + create(input: InsertTodo): Promise { + return this.todos.insertOne(input); + } + + find(id: Todo['id'], userId: Todo['userId']): Promise { + return this.todos + .findById(id, userId) + .then(definedOrNotFound('To-Do not found')); + } + + update( + id: Todo['id'], + userId: Todo['userId'], + input: UpdateTodo + ): Promise { + return this.todos + .updateById(id, userId, input) + .then(definedOrNotFound('To-Do not found')); + } + + delete(id: Todo['id'], userId: Todo['userId']): Promise { + return this.todos + .deleteById(id, userId) + .then(updatedOrNotFound('To-Do not found')); + } + + list( + userId: Todo['userId'], + query: ListTodosQuery + ): Promise> { + return this.todos.list(userId, query); + } +} diff --git a/node-cli/assets/express/example-app/src/api/todos/sorters/index.ts b/node-cli/assets/express/example-app/src/api/todos/sorters/index.ts new file mode 100644 index 00000000..18b954f5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/sorters/index.ts @@ -0,0 +1,2 @@ +export * from './todos-by-created-at.sorter'; +export * from './todos-by-name.sorter'; diff --git a/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.test.ts b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.test.ts new file mode 100644 index 00000000..169221d7 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.test.ts @@ -0,0 +1,14 @@ +import { sortByCreatedAt } from './todos-by-created-at.sorter'; + +const queryBuilder = { + orderBy: jest.fn(), +}; + +describe('sortByCreatedAt', () => { + const order = 'asc'; + + it('should sort by createdAt', () => { + sortByCreatedAt(queryBuilder as never, order); + expect(queryBuilder.orderBy).toHaveBeenCalledWith('createdAt', order); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.ts b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.ts new file mode 100644 index 00000000..039f5dbf --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-created-at.sorter.ts @@ -0,0 +1,5 @@ +import { Tables } from 'knex/types/tables'; +import { Sorter } from '@utils/query'; + +export const sortByCreatedAt: Sorter = (qb, order) => + qb.orderBy('createdAt', order); diff --git a/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.test.ts b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.test.ts new file mode 100644 index 00000000..8db1a80f --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.test.ts @@ -0,0 +1,14 @@ +import { sortByName } from './todos-by-name.sorter'; + +const queryBuilder = { + orderBy: jest.fn(), +}; + +describe('sortByName', () => { + const order = 'desc'; + + it('should sort by name', () => { + sortByName(queryBuilder as never, order); + expect(queryBuilder.orderBy).toHaveBeenCalledWith('name', order); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.ts b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.ts new file mode 100644 index 00000000..da9aab29 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/todos/sorters/todos-by-name.sorter.ts @@ -0,0 +1,5 @@ +import { Tables } from 'knex/types/tables'; +import { Sorter } from '@utils/query'; + +export const sortByName: Sorter = (qb, order) => + qb.orderBy('name', order); diff --git a/node-cli/assets/express/example-app/src/api/users/entities/index.ts b/node-cli/assets/express/example-app/src/api/users/entities/index.ts new file mode 100644 index 00000000..6390588a --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/entities/index.ts @@ -0,0 +1,3 @@ +export * from './insert-user.entity'; +export * from './update-user.entity'; +export * from './user.entity'; diff --git a/node-cli/assets/express/example-app/src/api/users/entities/insert-user.entity.ts b/node-cli/assets/express/example-app/src/api/users/entities/insert-user.entity.ts new file mode 100644 index 00000000..36c73741 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/entities/insert-user.entity.ts @@ -0,0 +1,4 @@ +import { Insert } from '@database/operations'; +import { User } from './user.entity'; + +export type InsertUser = Insert>; diff --git a/node-cli/assets/express/example-app/src/api/users/entities/update-user.entity.ts b/node-cli/assets/express/example-app/src/api/users/entities/update-user.entity.ts new file mode 100644 index 00000000..a83848a4 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/entities/update-user.entity.ts @@ -0,0 +1,4 @@ +import { Update } from '@database/operations'; +import { User } from './user.entity'; + +export type UpdateUser = Update>; diff --git a/node-cli/assets/express/example-app/src/api/users/entities/user.entity.ts b/node-cli/assets/express/example-app/src/api/users/entities/user.entity.ts new file mode 100644 index 00000000..a86de3f4 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/entities/user.entity.ts @@ -0,0 +1,8 @@ +export interface User { + id: number; + userId: string; + email: string; + password: string; + createdAt: string; + updatedAt: string; +} diff --git a/node-cli/assets/express/example-app/src/api/users/error-mappings/index.ts b/node-cli/assets/express/example-app/src/api/users/error-mappings/index.ts new file mode 100644 index 00000000..9e6f56e6 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './user-email-taken.error-mapping'; diff --git a/node-cli/assets/express/example-app/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/node-cli/assets/express/example-app/src/api/users/error-mappings/user-email-taken.error-mapping.ts new file mode 100644 index 00000000..d3b5f779 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/error-mappings/user-email-taken.error-mapping.ts @@ -0,0 +1,6 @@ +import { uniqueViolation } from '@database/error-mappings'; + +export const UserEmailTaken = uniqueViolation( + 'unq_users_email', + 'User email already taken' +); diff --git a/node-cli/assets/express/example-app/src/api/users/index.ts b/node-cli/assets/express/example-app/src/api/users/index.ts new file mode 100644 index 00000000..ec11d0a5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/index.ts @@ -0,0 +1,4 @@ +export * from './entities'; +export * from './error-mappings'; +export * from './repositories'; +export * from './schemas'; diff --git a/node-cli/assets/express/example-app/src/api/users/repositories/__mocks__/users.repository.ts b/node-cli/assets/express/example-app/src/api/users/repositories/__mocks__/users.repository.ts new file mode 100644 index 00000000..46277b1e --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/repositories/__mocks__/users.repository.ts @@ -0,0 +1,34 @@ +import { DuplicateRecordError } from '@database/errors'; +import { InsertUser, User } from '../../entities'; + +export class UsersRepository { + private lastId = 0; + private records: User[] = []; + + private nextId() { + return ++this.lastId; + } + + async insertOne(input: InsertUser): Promise { + const existingRecord = await this.findByEmail(input.email); + + if (existingRecord) { + throw new DuplicateRecordError('User Email already taken'); + } + + const record = { + id: this.nextId(), + ...input, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + this.records.push(record); + + return record; + } + + async findByEmail(email: string): Promise { + return this.records.find((r) => r.email === email); + } +} diff --git a/node-cli/assets/express/example-app/src/api/users/repositories/index.ts b/node-cli/assets/express/example-app/src/api/users/repositories/index.ts new file mode 100644 index 00000000..96798590 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/repositories/index.ts @@ -0,0 +1 @@ +export * from './users.repository'; diff --git a/node-cli/assets/express/example-app/src/api/users/repositories/users.repository.test.ts b/node-cli/assets/express/example-app/src/api/users/repositories/users.repository.test.ts new file mode 100644 index 00000000..8dcff4be --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/repositories/users.repository.test.ts @@ -0,0 +1,79 @@ +import Knex from 'knex'; +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { DuplicateRecordError } from '@database/errors'; +import { User } from '../entities/user.entity'; +import { UsersRepository } from './users.repository'; + +describe('UsersRepository', () => { + const knex = Knex({ client: 'pg', connection: {} }); + const users = new UsersRepository(knex); + const usersQb = knex('users'); + + describe('insertOne', () => { + it('should return the newly created record', async () => { + const user = { email: 'email@example.com', password: '123', userId: '1' }; + + jest.spyOn(usersQb, 'insert'); + jest.spyOn(usersQb, 'returning'); + jest.spyOn(usersQb, 'then'); + jest.spyOn(usersQb, 'catch').mockImplementationOnce(async () => ({ + id: 1, + ...user, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })); + + const result = await users.insertOne(user); + + expect(usersQb.insert).toHaveBeenCalledWith(user); + expect(usersQb.returning).toHaveBeenCalledWith('*'); + expect(usersQb.then).toHaveBeenCalled(); + expect(usersQb.catch).toHaveBeenCalled(); + + expect(result).toEqual(expect.objectContaining(user)); + }); + + it('should rethrow email uniq constraint violation as duplicate record', () => { + const error = new DatabaseError('unique violation', 72, 'error'); + error.code = PostgresError.UNIQUE_VIOLATION; + error.constraint = 'unq_users_email'; + const thenable = () => ({ then: () => Promise.reject(error) }); + + jest + .spyOn(usersQb, 'returning') + .mockImplementationOnce(thenable as never); + + expect( + users.insertOne({ email: 'email@example.com', password: '123', userId: '1' }) + ).rejects.toThrow( + new DuplicateRecordError('User email already taken') + ); + }); + }); + + describe('findByEmail', () => { + it('should return the first record found', async () => { + const user: User = { + id: 1, + email: 'email@example.com', + userId: '1', + password: '123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + jest.spyOn(usersQb, 'where'); + jest + .spyOn(usersQb, 'first') + .mockImplementationOnce(() => Promise.resolve(user) as never); + + const result = await users.findByEmail(user.email); + + expect(usersQb.where).toHaveBeenCalledWith({ email: user.email }); + expect(usersQb.first).toHaveBeenCalled(); + + expect(result).toEqual(user); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/api/users/repositories/users.repository.ts b/node-cli/assets/express/example-app/src/api/users/repositories/users.repository.ts new file mode 100644 index 00000000..03874069 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/repositories/users.repository.ts @@ -0,0 +1,20 @@ +import { Knex } from 'knex'; +import { rethrowError } from '@utils/error'; +import { InsertUser, User } from '../entities'; +import { UserEmailTaken } from '../error-mappings'; + +export class UsersRepository { + constructor(private readonly knex: Knex) {} + + async insertOne(input: InsertUser): Promise { + return await this.knex('users') + .insert(input) + .returning('*') + .then(([user]) => user) + .catch(rethrowError(UserEmailTaken)); + } + + async findByEmail(email: User['email']): Promise { + return await this.knex('users').where({ email }).first(); + } +} diff --git a/node-cli/assets/express/example-app/src/api/users/schemas/index.ts b/node-cli/assets/express/example-app/src/api/users/schemas/index.ts new file mode 100644 index 00000000..d0035833 --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/schemas/index.ts @@ -0,0 +1,2 @@ +// TODO: check if this can be deleted +export * from './user.schema'; diff --git a/node-cli/assets/express/example-app/src/api/users/schemas/user.schema.ts b/node-cli/assets/express/example-app/src/api/users/schemas/user.schema.ts new file mode 100644 index 00000000..4d6a96de --- /dev/null +++ b/node-cli/assets/express/example-app/src/api/users/schemas/user.schema.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; +import { email, password, withId, withTimestamps } from '@utils/validation'; + +export const userSchema = z + .object({ + email: email, + password: password, + userId: z.string(), + }) + .merge(withId) + .merge(withTimestamps) + .openapi({ ref: 'User' }); diff --git a/node-cli/assets/express/example-app/src/app.test.ts b/node-cli/assets/express/example-app/src/app.test.ts new file mode 100644 index 00000000..30d5cd95 --- /dev/null +++ b/node-cli/assets/express/example-app/src/app.test.ts @@ -0,0 +1,22 @@ +import { create } from './app'; + +describe('create', () => { + it('should return an app instance and a destroy function', () => { + const result = create({ + PORT: 3000, + REQUEST_LOGGING: false, + ERROR_LOGGING: false, + JWT_EXPIRATION: 1, + JWT_SECRET: 'secret', + NODE_ENV: 'test', + PGDATABASE: 'database', + PGHOST: 'localhost', + PGPASSWORD: 'password', + PGPORT: 5432, + PGUSER: 'user', + }); + + expect(result.app).toBeDefined(); + expect(result.destroy).toBeDefined(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/app.ts b/node-cli/assets/express/example-app/src/app.ts new file mode 100644 index 00000000..7e2bfb9b --- /dev/null +++ b/node-cli/assets/express/example-app/src/app.ts @@ -0,0 +1,137 @@ +import express, { json, Request, RequestHandler } from 'express'; +import helmet from 'helmet'; +import cors from 'cors'; +import compression from 'compression'; +import pino from 'pino'; +import queryType from 'query-types'; +import { NotFound, Conflict, Unauthorized } from 'http-errors'; +import { UnauthorizedError } from 'express-jwt'; + +import { Environment } from '@utils/environment'; +import { + onInit as initDatabase, + create as createDbClient, + destroy as destroyDbClient, + RecordNotFoundError, + DuplicateRecordError, +} from '@database'; +import { + handleError, + mapError, + logRequest, + validateRequest, + attachServices, + validateJwt, +} from '@middleware'; + +import { + JwtService, + PasswordService, + AuthService, + authRoutes, +} from '@api/auth'; +import { healthcheckRoutes } from '@api/healthchecks'; +import { TodosRepository, TodosService, todoRoutes } from '@api/todos'; +import { UsersRepository } from '@api/users'; + +export function create(env: Environment) { + // init modules + initDatabase(); + + // create a logger + const logger = pino({ + name: 'http', + ...(env.NODE_ENV === 'development' && { + transport: { + target: 'pino-pretty' + }, + }), + }); + + // create services + const dbClient = createDbClient(); + const usersRepository = new UsersRepository(dbClient); + const todosRepository = new TodosRepository(dbClient); + const jwtService = new JwtService(env); + const passwordService = new PasswordService(); + const authService = new AuthService( + usersRepository, + jwtService, + passwordService + ); + const todosService = new TodosService(todosRepository); + const services: Request['services'] = { todosService, authService }; + + // create the app + const app = express(); + + if (env['REQUEST_LOGGING']) { + app.use(logRequest(logger)); + } + + app.use( + // add security HTTP headers + helmet(), + // enables CORS + cors(/* TODO: configure origins */), + // parses the body of application/json requests + json(), + // compresses response bodies + compression(), + // makes the services available to the route handlers by attaching them to the request + attachServices(services), + // handles numeric and boolean values for Express req.query object + queryType.middleware() + ); + + // flatten all routes into an array + // alternatively, a separate router instance can be used + // for each group of routes (group by prefix) + const routes = [ + ...healthcheckRoutes, + ...authRoutes, + ...todoRoutes, + ]; + + // register routes + for (const { + method, + path, + request, + authenticate = false, + middleware = [], + handler, + } of routes) { + if (authenticate) { + middleware.push(validateJwt(env.JWT_SECRET)); + } + + if (request) { + middleware.push(validateRequest(request)); + } + + // eslint-disable-next-line security/detect-object-injection + app[method]( + path, + ...(middleware as RequestHandler[]), + handler as RequestHandler + ); + } + + // register error handlers + app.use( + mapError({ + [RecordNotFoundError.name]: NotFound, + [DuplicateRecordError.name]: Conflict, + [UnauthorizedError.name]: Unauthorized, + }), + handleError(logger, env['ERROR_LOGGING']) + ); + + // define an app tear down function + const destroy = async () => { + await destroyDbClient(dbClient); + }; + + return { app, destroy }; +} diff --git a/node-cli/assets/express/example-app/src/database/client.test.ts b/node-cli/assets/express/example-app/src/database/client.test.ts new file mode 100644 index 00000000..53c371ad --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/client.test.ts @@ -0,0 +1,23 @@ +import Knex from 'knex'; +import { create, destroy } from './client'; + +describe('create', () => { + it('should construct a new client', () => { + create(); + expect(Knex).toHaveBeenCalledWith( + expect.objectContaining({ + client: 'pg', + connection: {}, + pool: { min: 0 }, + }) + ); + }); +}); + +describe('destroy', () => { + it('should call destroy on the client', () => { + const client = { destroy: jest.fn() }; + destroy(client as never); + expect(client.destroy).toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/database/client.ts b/node-cli/assets/express/example-app/src/database/client.ts new file mode 100644 index 00000000..92a56f98 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/client.ts @@ -0,0 +1,23 @@ +import Knex, { Knex as TKnex } from 'knex'; + +export const create = () => + Knex({ + client: 'pg', + // We don't need to specify the connection options as we're using the default env var names + // see https://node-postgres.com/features/connecting#environment-variables + // see https://www.postgresql.org/docs/9.1/libpq-envars.html + connection: {}, + pool: { + // the minimum is for all connections rather than alive, so it has to be set to zero + // see https://knexjs.org/guide/#pool + min: 0, + }, + migrations: { + directory: './migrations', + }, + seeds: { + directory: `./src/database/seeds/${process.env.NODE_ENV}`, + }, + }); + +export const destroy = (knex: TKnex) => knex.destroy(); diff --git a/node-cli/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts b/node-cli/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts new file mode 100644 index 00000000..0e4a5eee --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.test.ts @@ -0,0 +1,46 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { RecordNotFoundError } from '../errors'; +import { foreignKeyViolation } from './foreign-key-violation.error-mapping'; + +describe('foreignKeyViolation', () => { + it('should return an error mapping', () => { + expect(foreignKeyViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = foreignKeyViolation( + 'constraint-name', + 'error-message' + ).isError; + + it('should return true when the error is a foreign key constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.FOREIGN_KEY_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a foreign key constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = foreignKeyViolation( + 'constraint-name', + 'error-message' + ).newError; + + it('should return an instance of RecordNotFound with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(RecordNotFoundError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/node-cli/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.ts new file mode 100644 index 00000000..ede30de6 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/error-mappings/foreign-key-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { RecordNotFoundError } from '@database/errors'; +import { isForeignKeyViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const foreignKeyViolation = ( + constraint: string, + message: string +): ErrorMapping => ({ + isError: isForeignKeyViolation(constraint), + newError: () => new RecordNotFoundError(message), +}); diff --git a/node-cli/assets/express/example-app/src/database/error-mappings/index.ts b/node-cli/assets/express/example-app/src/database/error-mappings/index.ts new file mode 100644 index 00000000..9f743b83 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/error-mappings/index.ts @@ -0,0 +1,2 @@ +export * from './foreign-key-violation.error-mapping'; +export * from './unique-violation.error-mapping'; diff --git a/node-cli/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.test.ts b/node-cli/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.test.ts new file mode 100644 index 00000000..ec0e98c0 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.test.ts @@ -0,0 +1,43 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { DuplicateRecordError } from '../errors'; +import { uniqueViolation } from './unique-violation.error-mapping'; + +describe('uniqueViolation', () => { + it('should return an error mapping', () => { + expect(uniqueViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = uniqueViolation('constraint-name', 'error-message').isError; + + it('should return true when the error is a unique constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.UNIQUE_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a unique constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = uniqueViolation( + 'constraint-name', + 'error-message' + ).newError; + + it('should return an instance of DuplicateRecord with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(DuplicateRecordError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.ts b/node-cli/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.ts new file mode 100644 index 00000000..df298502 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/error-mappings/unique-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { DuplicateRecordError } from '@database/errors'; +import { isUniqueViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const uniqueViolation = ( + constraint: string, + message: string +): ErrorMapping => ({ + isError: isUniqueViolation(constraint), + newError: () => new DuplicateRecordError(message), +}); diff --git a/node-cli/assets/express/example-app/src/database/errors/duplicate-record.error.ts b/node-cli/assets/express/example-app/src/database/errors/duplicate-record.error.ts new file mode 100644 index 00000000..65fbab07 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/errors/duplicate-record.error.ts @@ -0,0 +1,6 @@ +export class DuplicateRecordError extends Error { + constructor(message: string) { + super(message); + this.name = DuplicateRecordError.name; + } +} diff --git a/node-cli/assets/express/example-app/src/database/errors/index.ts b/node-cli/assets/express/example-app/src/database/errors/index.ts new file mode 100644 index 00000000..7099d226 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/errors/index.ts @@ -0,0 +1,2 @@ +export * from './duplicate-record.error'; +export * from './record-not-found.error'; diff --git a/node-cli/assets/express/example-app/src/database/errors/record-not-found.error.ts b/node-cli/assets/express/example-app/src/database/errors/record-not-found.error.ts new file mode 100644 index 00000000..52bcb4fa --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/errors/record-not-found.error.ts @@ -0,0 +1,6 @@ +export class RecordNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = RecordNotFoundError.name; + } +} diff --git a/node-cli/assets/express/example-app/src/database/index.ts b/node-cli/assets/express/example-app/src/database/index.ts new file mode 100644 index 00000000..4de1612b --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/index.ts @@ -0,0 +1,7 @@ +export * from './client'; +export * from './error-mappings'; +export * from './errors'; +export * from './on-init'; +export * from './operations'; +export * from './query-builder'; +export * from './utils'; diff --git a/node-cli/assets/express/example-app/src/database/on-init.ts b/node-cli/assets/express/example-app/src/database/on-init.ts new file mode 100644 index 00000000..1624cd1f --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/on-init.ts @@ -0,0 +1,8 @@ +import pg from 'pg'; + +export const onInit = () => { + // https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js + pg.types.setTypeParser(pg.types.builtins.INT8, parseInt); + pg.types.setTypeParser(pg.types.builtins.NUMERIC, parseFloat); + pg.types.setTypeParser(pg.types.builtins.DATE, (v) => v); // keep as string for now +}; diff --git a/node-cli/assets/express/example-app/src/database/operations.ts b/node-cli/assets/express/example-app/src/database/operations.ts new file mode 100644 index 00000000..dd2cd033 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/operations.ts @@ -0,0 +1,14 @@ +import { NullableKeysPartial } from '@utils/types'; + +// Nullable means both that you can put a null and that the default is null. +// Having a default value means that the key can be omitted (i.e. is optional), +// hence, all nullables keys are optional +/** + * Makes nullable keys optional (partial) as well + */ +export type Insert = NullableKeysPartial; + +/** + * Makes all keys optional (partial) + */ +export type Update = Partial; diff --git a/node-cli/assets/express/example-app/src/database/query-builder/extensions.test.ts b/node-cli/assets/express/example-app/src/database/query-builder/extensions.test.ts new file mode 100644 index 00000000..a0c79519 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/query-builder/extensions.test.ts @@ -0,0 +1,78 @@ +import { Sort } from '@utils/query'; +import { filter, sort, paginate } from './extensions'; + +describe('filter', () => { + it('should apply all the filters to the query builder', () => { + const queryBuilder = {}; + + const filters = { + name: 'John', + height: undefined, + age: 42, + }; + + const filterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _name: string) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _age: number) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + height: jest.fn((qb, _height: number) => qb), + }; + + filter(queryBuilder as never, filters, filterMap as never); + + expect(filterMap.name).toHaveBeenCalledWith(queryBuilder, filters.name); + expect(filterMap.height).not.toHaveBeenCalled(); + expect(filterMap.age).toHaveBeenCalledWith(queryBuilder, filters.age); + }); +}); + +describe('sort', () => { + it('should apply all the sortings to the query builder', () => { + const queryBuilder = {}; + + const sorts: Sort<'name' | 'age'>[] = [ + { column: 'name' }, + { column: 'age', order: 'desc' }, + ]; + + const sorterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _order) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _order) => qb), + }; + + sort(queryBuilder as never, sorts, sorterMap); + + expect(sorterMap.name).toHaveBeenCalledWith(queryBuilder, 'asc'); + expect(sorterMap.age).toHaveBeenCalledWith(queryBuilder, 'desc'); + }); +}); + +describe('paginate', () => { + it('should apply an offset and a limit to the query builder', () => { + const queryBuilder = { + offset() { + return this; + }, + limit() { + return this; + }, + }; + + jest.spyOn(queryBuilder, 'offset'); + jest.spyOn(queryBuilder, 'limit'); + + const pagination = { + page: 3, + items: 15, + }; + + paginate(queryBuilder as never, pagination); + + expect(queryBuilder.offset).toHaveBeenCalledWith(30); + expect(queryBuilder.limit).toHaveBeenCalledWith(15); + }); +}); diff --git a/node-cli/assets/express/example-app/src/database/query-builder/extensions.ts b/node-cli/assets/express/example-app/src/database/query-builder/extensions.ts new file mode 100644 index 00000000..c6ece085 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/query-builder/extensions.ts @@ -0,0 +1,48 @@ +import { Knex } from 'knex'; +import { + Sort, + Pagination, + FilterMap, + SorterMap, + extractPagination, +} from '@utils/query'; + +// +// Knex.QueryBuilder extensions +// +export const filter = < + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + Query extends Record, + Filters extends FilterMap +>( + qb: Knex.QueryBuilder, + filters: Query, + filterMap: Filters +): Knex.QueryBuilder => + Object.entries(filters) + .filter(([, v]) => v !== undefined) + .reduce>( + (qb, [k, v]) => filterMap[k as keyof Filters](qb, v as never), + qb + ); + +// eslint-disable-next-line @typescript-eslint/ban-types +export const sort = ( + qb: Knex.QueryBuilder, + sorts: Sort[], + sorterMap: SorterMap +): Knex.QueryBuilder => + sorts.reduce>( + (qb, sort) => sorterMap[sort.column](qb, sort.order || 'asc'), + qb + ); + +export const paginate = ( + qb: QB, + pagination?: Pagination +) => { + const { page, items } = extractPagination(pagination); + + return qb.offset((page - 1) * items).limit(items); +}; diff --git a/node-cli/assets/express/example-app/src/database/query-builder/index.ts b/node-cli/assets/express/example-app/src/database/query-builder/index.ts new file mode 100644 index 00000000..4509f5cc --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/query-builder/index.ts @@ -0,0 +1 @@ +export * from './extensions'; diff --git a/node-cli/assets/express/example-app/src/database/seeds/test/index.ts b/node-cli/assets/express/example-app/src/database/seeds/test/index.ts new file mode 100644 index 00000000..fa2d4088 --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/seeds/test/index.ts @@ -0,0 +1,38 @@ +import { Knex } from "knex"; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.seed = async function (knex: Knex) { + // Deletes ALL existing entries + await knex('users').insert([ + // The original password for this hash is 'pass@ord' + { + email: 'hello@email.com', + password: '$2b$10$Mxur7NOiTlm22yuldEMZgOCbIV7bxDCcUbBLFbzrJ1MrnIczZB.92', // pragma: allowlist secret + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); + + await knex('todos').insert([ + { + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 2', + note: 'Buy detergent 2', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 3', + note: 'Buy detergent 3', + completed: true, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); +}; diff --git a/node-cli/assets/express/example-app/src/database/utils/error.ts b/node-cli/assets/express/example-app/src/database/utils/error.ts new file mode 100644 index 00000000..38cf35ea --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/utils/error.ts @@ -0,0 +1,17 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; + +export const isDatabaseError = (e: unknown): e is DatabaseError => + e instanceof DatabaseError && + e.code !== undefined && + e.constraint !== undefined; + +export const isUniqueViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.UNIQUE_VIOLATION && + e.constraint === constraint; + +export const isForeignKeyViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.FOREIGN_KEY_VIOLATION && + e.constraint === constraint; diff --git a/node-cli/assets/express/example-app/src/database/utils/index.ts b/node-cli/assets/express/example-app/src/database/utils/index.ts new file mode 100644 index 00000000..93ae819e --- /dev/null +++ b/node-cli/assets/express/example-app/src/database/utils/index.ts @@ -0,0 +1 @@ +export * from './error'; diff --git a/node-cli/assets/express/example-app/src/extensions/knex/index.ts b/node-cli/assets/express/example-app/src/extensions/knex/index.ts new file mode 100644 index 00000000..a826b4c4 --- /dev/null +++ b/node-cli/assets/express/example-app/src/extensions/knex/index.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +import Knex from 'knex'; +import { filter, sort, paginate } from '@database/query-builder'; +import { FilterMap, Pagination, Sort, SorterMap } from '@utils/query'; + +declare module 'knex' { + namespace Knex { + interface QueryBuilder { + /** + * Given a column-to-value map and column-to-filter map, applies the filters to the query builder. + * Filter map must cover all columns in the column-to-value map. + * + * Example: + * ``` + * queryBuilder.filter( + * { + * name: 'John', + * isAncient: true, + * }, + * { + * name: (queryBuilder, name) => queryBuilder.whereILike('name', `%${name}%`), + * isAncient: (queryBuilder, isAncient) => queryBuilder.where('age', isAncient ? '>=' : '<', 100), + * } + * ); + * ``` + */ + filter( + filters: Filters | undefined, + filterMap: FilterMap + ): this; + /** + * Given a list of column sortings an a column-to-sorter map, applies the sorters to the query builder. + * Sorter map must cover all columns in the column sortings list. + * + * Example: + * ``` + * queryBuilder.sort( + * [ + * { column: 'name' }, + * { column: 'age', order: 'desc'} + * ], + * { + * name: (queryBuilder, order) => queryBuilder.orderBy('name', order), + * age: (queryBuilder, order) => queryBuilder.orderBy('age', order), + * } + * ); + * ``` + */ + sort( + sorts: Sort[] | undefined, + sorterMap: SorterMap + ): this; + /** + * Offset pagination. This a shorthand for the following: + * + * ``` + * queryBuilder + * .offset((pagination.page - 1) * pagination.items) + * .limit(pagination.items); + * ``` + */ + paginate(pagination?: Pagination): this; + } + } +} + +export const register = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.filter) { + Knex.QueryBuilder.extend('filter', function (filters, filterMap) { + return filters ? filter(this, filters, filterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.sort) { + Knex.QueryBuilder.extend('sort', function (sorts, sorterMap) { + return sorts ? sort(this, sorts || [], sorterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.paginate) { + Knex.QueryBuilder.extend('paginate', function (pagination) { + return paginate(this, pagination); + }); + } +}; diff --git a/node-cli/assets/express/example-app/src/extensions/knex/register.ts b/node-cli/assets/express/example-app/src/extensions/knex/register.ts new file mode 100644 index 00000000..07230200 --- /dev/null +++ b/node-cli/assets/express/example-app/src/extensions/knex/register.ts @@ -0,0 +1,3 @@ +import { register } from './index'; + +register(); diff --git a/node-cli/assets/express/example-app/src/extensions/zod/index.ts b/node-cli/assets/express/example-app/src/extensions/zod/index.ts new file mode 100644 index 00000000..5bead0fb --- /dev/null +++ b/node-cli/assets/express/example-app/src/extensions/zod/index.ts @@ -0,0 +1,24 @@ +import z from 'zod'; +import { extendZodWithOpenApi } from 'zod-openapi'; + +declare module 'zod' { + interface ZodNumber { + coerce(): ZodNumber; + } +} + +export const register = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (typeof z.ZodNumber.prototype.coerce === 'undefined') { + z.ZodNumber.prototype.coerce = function () { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new (this as any).constructor({ + ...this._def, + coerce: true, + }); + }; + } + + extendZodWithOpenApi(z); +}; diff --git a/node-cli/assets/express/example-app/src/extensions/zod/register.ts b/node-cli/assets/express/example-app/src/extensions/zod/register.ts new file mode 100644 index 00000000..07230200 --- /dev/null +++ b/node-cli/assets/express/example-app/src/extensions/zod/register.ts @@ -0,0 +1,3 @@ +import { register } from './index'; + +register(); diff --git a/node-cli/assets/express/example-app/src/index.ts b/node-cli/assets/express/example-app/src/index.ts new file mode 100644 index 00000000..8923f99a --- /dev/null +++ b/node-cli/assets/express/example-app/src/index.ts @@ -0,0 +1,45 @@ +// register extensions as the very first thing in the entry point +import '@extensions/zod/register'; +import '@extensions/knex/register'; + +import { createHttpTerminator } from 'http-terminator'; + +import { environmentSchema } from '@utils/environment'; +import { create as createApp } from './app'; + +function bootstrap() { + // validate environment and freeze it + const env = Object.freeze(environmentSchema.parse(process.env)); + + // create the application + const { app, destroy: destroyApp } = createApp(env); + + // start server + const server = app.listen(env.PORT, () => { + console.log(`App is running on http://localhost:${env.PORT}`); + }); + + // setup graceful shutdown + const httpTerminator = createHttpTerminator({ server }); + + const shutdown = async () => { + console.log('Shutting down...'); + + // process in-progress requests + await httpTerminator.terminate(); + + // then destroy the application + await destroyApp(); + }; + + const onSignal = (signal: NodeJS.Signals) => { + console.log(`${signal} received`); + shutdown(); + }; + + // attach signal listeners + process.on('SIGTERM', onSignal); + process.on('SIGINT', onSignal); +} + +bootstrap(); diff --git a/node-cli/assets/express/example-app/src/middleware/attach-services.middleware.test.ts b/node-cli/assets/express/example-app/src/middleware/attach-services.middleware.test.ts new file mode 100644 index 00000000..e718dfb3 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/attach-services.middleware.test.ts @@ -0,0 +1,15 @@ +import { attachServices } from './attach-services.middleware'; + +describe('attachServices', () => { + it('should assign the services to the request', () => { + const services = {}; + const req = {}; + const next = jest.fn(); + + const middleware = attachServices(services as never); + middleware(req as never, {} as never, next); + + expect((req as { services: typeof services }).services).toBe(services); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/middleware/attach-services.middleware.ts b/node-cli/assets/express/example-app/src/middleware/attach-services.middleware.ts new file mode 100644 index 00000000..20bacf41 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/attach-services.middleware.ts @@ -0,0 +1,8 @@ +import { Request, RequestHandler } from 'express'; + +export const attachServices = + (services: Request['services']): RequestHandler => + (req, _res, next) => { + req.services = services; + next(); + }; diff --git a/node-cli/assets/express/example-app/src/middleware/handle-error.middleware.test.ts b/node-cli/assets/express/example-app/src/middleware/handle-error.middleware.test.ts new file mode 100644 index 00000000..f8fd61b3 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/handle-error.middleware.test.ts @@ -0,0 +1,70 @@ +import createError from 'http-errors'; +import { handleError } from './handle-error.middleware'; + +describe('handleError', () => { + it('should return an error handling middleware', () => { + const middleware = handleError({} as never, false); + expect(middleware).toBeInstanceOf(Function); + expect(middleware.length).toBe(4); + }); + + describe('middleware', () => { + const middleware = handleError({} as never, false); + + describe('when the response headers have been sent', () => { + it('should call next with the error', () => { + const error = new Error(); + const next = jest.fn(); + middleware(error, {} as never, { headersSent: true } as never, next); + }); + }); + + describe('when the response headers have not been sent yet', () => { + describe('and the error is an http-errors error', () => { + const error = createError.UnprocessableEntity(); + error.errors = []; + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + beforeAll(() => { + middleware(error, {} as never, res as never, () => undefined); + }); + + it('should set the response status to the error statusCode', () => { + expect(status).toHaveBeenCalledWith(error.statusCode); + }); + + it('should respond with an object with the error message', () => { + expect(send).toHaveBeenCalledWith({ + message: error.message, + errors: error.errors, + }); + }); + }); + + describe('but the error is not an http-errors error', () => { + const logger = { error: jest.fn() }; + const middleware = handleError(logger as never, true); + const error = new Error('message'); + const send = jest.fn(); + const status = jest.fn(() => ({ send })); + const res = { send, status }; + + beforeAll(() => { + middleware(error, {} as never, res as never, () => undefined); + }); + + it('should log the error', () => { + expect(logger.error).toHaveBeenCalledWith(error); + }); + + it('should respond with a 500 error with a predefined message', () => { + expect(send).toHaveBeenCalledWith({ + message: 'Internal Server Error', + }); + }); + }); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/middleware/handle-error.middleware.ts b/node-cli/assets/express/example-app/src/middleware/handle-error.middleware.ts new file mode 100644 index 00000000..5d8352cb --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/handle-error.middleware.ts @@ -0,0 +1,29 @@ +import { ErrorRequestHandler } from 'express'; +import { Logger } from 'pino'; +import createError from 'http-errors'; + +export const handleError = function (logger: Logger, logErrors: boolean): ErrorRequestHandler { + return function (err, _req, res, next) { + // https://expressjs.com/en/guide/error-handling.html + // If you call next() with an error after you have started writing the response (for example, if you encounter an error while streaming + // the response to the client) the Express default error handler closes the connection and fails the request. + // So when you add a custom error handler, you must delegate to the default Express error handler, when the headers have already + // been sent to the client + if (res.headersSent) { + return next(err); + } + + if (logErrors) { + logger.error(err); + } + + if (createError.isHttpError(err)) { + res.status(err.statusCode).send({ + message: err.message, + ...(err.errors && { errors: err.errors }), + }); + } else { + res.status(500).send({ message: 'Internal Server Error' }); + } + }; +}; diff --git a/node-cli/assets/express/example-app/src/middleware/index.ts b/node-cli/assets/express/example-app/src/middleware/index.ts new file mode 100644 index 00000000..49ae6e99 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/index.ts @@ -0,0 +1,6 @@ +export * from './attach-services.middleware'; +export * from './handle-error.middleware'; +export * from './log-request.middleware'; +export * from './map-error.middleware'; +export * from './validate-jwt.middleware'; +export * from './validate-request.middleware'; diff --git a/node-cli/assets/express/example-app/src/middleware/log-request.middleware.test.ts b/node-cli/assets/express/example-app/src/middleware/log-request.middleware.test.ts new file mode 100644 index 00000000..9fb371d1 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/log-request.middleware.test.ts @@ -0,0 +1,33 @@ +import { logRequest } from './log-request.middleware'; +import { Logger } from 'pino'; +import { Request, Response } from 'express'; + +describe('logRequest', () => { + it('should log the request details', () => { + const logger: Partial = { info: jest.fn() }; + const req: Partial = { + method: 'GET', + url: '/todos', + body: { password: 'secret' }, + headers: { Authorization: 'Bearer token', Cookie: 'cookie' }, + ip: '127.0.0.1' + }; + const res: Partial = { on: jest.fn().mockImplementation((event, cb) => cb()) }; + const next = jest.fn(); + + logRequest(logger as Logger)(req as Request, res as Response, next); + + const expectedLogMsg = { + timestamp: expect.any(String), + duration: expect.any(String), + ip: req.ip, + headers: { Authorization: '[[REMOVED]]', Cookie: '[[REMOVED]]' }, + method: req.method, + url: req.url, + body: { password: '[[REMOVED]]' }, + }; + + expect(logger.info).toHaveBeenCalledWith(expectedLogMsg); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/middleware/log-request.middleware.ts b/node-cli/assets/express/example-app/src/middleware/log-request.middleware.ts new file mode 100644 index 00000000..ae259b15 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/log-request.middleware.ts @@ -0,0 +1,55 @@ +import { Request, Response, NextFunction } from 'express'; +import { IncomingHttpHeaders } from 'http'; +import { performance } from 'perf_hooks'; +import { Logger } from 'pino'; + +const REMOVED = '[[REMOVED]]'; + +export function sanitizeHeaders(headers: IncomingHttpHeaders): IncomingHttpHeaders { + const sanitizedHeaders = { ...headers }; + + if (sanitizedHeaders.Authorization) { + sanitizedHeaders.Authorization = REMOVED; + } + + if (sanitizedHeaders.Cookie) { + sanitizedHeaders.Cookie = REMOVED; + } + + return sanitizedHeaders; +} + +export function sanitizeBody(body: IncomingHttpHeaders): IncomingHttpHeaders { + const sanitizedBody = { ...body }; + + if (sanitizedBody.password) { + sanitizedBody.password = REMOVED; + } + + return sanitizedBody; +} + +export const logRequest = (logger: Logger) => (req: Request, res: Response, next: NextFunction) => { + const timestamp = new Date().toISOString(); + const startTime = performance.now(); + + res.on('finish', () => { + const endTime = performance.now(); + const duration = endTime - startTime; + const { url, body, method, headers, ip } = req; + + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: sanitizeHeaders(headers), + method, + url, + body: sanitizeBody(body), + }; + + logger.info(logMsg); + }); + + next(); +} diff --git a/node-cli/assets/express/example-app/src/middleware/map-error.middleware.test.ts b/node-cli/assets/express/example-app/src/middleware/map-error.middleware.test.ts new file mode 100644 index 00000000..c67025f5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/map-error.middleware.test.ts @@ -0,0 +1,32 @@ +import { DuplicateRecordError, RecordNotFoundError } from '@database/errors'; +import { mapError } from './map-error.middleware'; + +describe('mapError', () => { + it('should return an error handling middleware', () => { + const middleware = mapError({}); + expect(middleware).toBeInstanceOf(Function); + expect(middleware.length).toBe(4); + }); + + describe('middleware', () => { + it('should pass the error to next when no mapping for it is provided', () => { + const middleware = mapError({}); + const error = new Error(); + const next = jest.fn(); + middleware(error, {} as never, {} as never, next); + expect(next).toHaveBeenCalledWith(error); + }); + + it('should map the error and pass it to next when a mapping is provided', () => { + const middleware = mapError({ + [RecordNotFoundError.name]: DuplicateRecordError, + }); + const error = new RecordNotFoundError('message'); + const next = jest.fn(); + middleware(error, {} as never, {} as never, next); + expect(next).toHaveBeenCalledWith( + new DuplicateRecordError(new RecordNotFoundError('message').message) + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/middleware/map-error.middleware.ts b/node-cli/assets/express/example-app/src/middleware/map-error.middleware.ts new file mode 100644 index 00000000..f5534357 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/map-error.middleware.ts @@ -0,0 +1,12 @@ +import { ErrorRequestHandler } from 'express'; + +// TODO: utilize the mapError utility function +export const mapError = function ( + map: Record +): ErrorRequestHandler { + return function (err, _req, _res, next) { + const errorClass = map[err.name]; + const error = errorClass ? new errorClass(err.message) : err; + next(error); + }; +}; diff --git a/node-cli/assets/express/example-app/src/middleware/validate-jwt.middleware.test.ts b/node-cli/assets/express/example-app/src/middleware/validate-jwt.middleware.test.ts new file mode 100644 index 00000000..42945330 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/validate-jwt.middleware.test.ts @@ -0,0 +1,10 @@ +import { validateJwt } from './validate-jwt.middleware'; + +describe('validateAccessToken', () => { + it('should return an instance of the express-jwt middleware', () => { + const middleware = validateJwt('my-secret'); + + expect(middleware.name).toBe('middleware'); + expect(middleware.unless).toBeDefined(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/middleware/validate-jwt.middleware.ts b/node-cli/assets/express/example-app/src/middleware/validate-jwt.middleware.ts new file mode 100644 index 00000000..4583dc35 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/validate-jwt.middleware.ts @@ -0,0 +1,19 @@ +import { expressjwt } from 'express-jwt'; + +export const validateJwt = (secret: string) => + expressjwt({ + secret, + getToken: (req) => { + if (typeof req.headers.authorization !== 'string') { + return ''; + } + + const [type, token] = req.headers.authorization.split(' '); + if (type !== 'Bearer') { + return ''; + } + + return token; + }, + algorithms: ['HS256'], + }); diff --git a/node-cli/assets/express/example-app/src/middleware/validate-request.middleware.test.ts b/node-cli/assets/express/example-app/src/middleware/validate-request.middleware.test.ts new file mode 100644 index 00000000..bcd5c747 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/validate-request.middleware.test.ts @@ -0,0 +1,43 @@ +import z from 'zod'; +import { UnprocessableEntity } from 'http-errors'; +import { validateRequest } from './validate-request.middleware'; + +describe('validateJwt', () => { + describe('when the input is valid', () => { + const req = { body: { name: 'John', age: 42 } }; + const next = jest.fn(); + const schema = { + body: z.object({ name: z.string() }), + }; + const middleware = validateRequest(schema); + + beforeAll(() => { + middleware(req as never, {} as never, next); + }); + + it('should assign the validated input to the request', () => { + expect(req.body).toEqual({ name: 'John' }); + }); + + it('should call next', () => { + expect(next).toHaveBeenCalled(); + }); + }); + + describe('when the input is not vaid', () => { + const req = { body: { name: 42 } }; + const next = jest.fn(); + const schema = { + body: z.object({ name: z.string() }), + }; + const middleware = validateRequest(schema); + + beforeAll(() => { + middleware(req as never, {} as never, next); + }); + + it('should call next with the validation error', () => { + expect(next).toHaveBeenCalledWith(expect.any(UnprocessableEntity)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/middleware/validate-request.middleware.ts b/node-cli/assets/express/example-app/src/middleware/validate-request.middleware.ts new file mode 100644 index 00000000..f8e06ac5 --- /dev/null +++ b/node-cli/assets/express/example-app/src/middleware/validate-request.middleware.ts @@ -0,0 +1,24 @@ +import { RequestKey, RequestSchema } from '@utils/api'; +import { RequestHandler } from 'express'; +import createError from 'http-errors'; +import z from 'zod'; + +export const validateRequest = function ( + schema: RequestSchema +): RequestHandler { + return function (req, _res, next) { + const result = z.object(schema).safeParse(req); + + if (!result.success) { + const error = createError.UnprocessableEntity('Bad Data'); + error.errors = result.error.issues; + return next(error); + } + + for (const [k, v] of Object.entries(result.data)) { + req[k as RequestKey] = v; + } + + next(); + }; +}; diff --git a/node-cli/assets/express/example-app/src/utils/api/handler.test.ts b/node-cli/assets/express/example-app/src/utils/api/handler.test.ts new file mode 100644 index 00000000..b86cf0c2 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/handler.test.ts @@ -0,0 +1,20 @@ +import { asyncHandler } from './handler'; + +describe('asyncHandler', () => { + it('should pass the error to next when the promise returned by the handler is rejected', async () => { + const error = new Error(); + const next = jest.fn(); + const handler = asyncHandler(async () => { + throw error; + }); + await handler({} as never, {} as never, next); + expect(next).toHaveBeenCalledWith(error); + }); + + it('should not call next when the promise returned by the handler is resolved', async () => { + const next = jest.fn(); + const handler = asyncHandler(async () => null); + await handler({} as never, {} as never, next); + expect(next).not.toHaveBeenCalled(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/api/handler.ts b/node-cli/assets/express/example-app/src/utils/api/handler.ts new file mode 100644 index 00000000..94846a80 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/handler.ts @@ -0,0 +1,67 @@ +import { + RequestHandler as ExpressRequestHandler, + ErrorRequestHandler as ExpressErrorHandler, +} from 'express'; +import { AnyZodObject, z } from 'zod'; + +export type RequestKey = 'params' | 'query' | 'headers' | 'body'; + +export type RequestSchema< + Params extends AnyZodObject = AnyZodObject, + Query extends AnyZodObject = AnyZodObject, + Body extends AnyZodObject = AnyZodObject, + Headers extends AnyZodObject = AnyZodObject +> = { + params?: Params; + query?: Query; + body?: Body; + headers?: Headers; +}; + +export type RequestHandler = + ExpressRequestHandler< + z.infer>, + unknown, + z.infer>, + z.infer> + >; + +export type ErrorHandler = + ExpressErrorHandler< + z.infer>, + unknown, + z.infer>, + z.infer> + >; + +/** + * A utility function that does the `.catch(next)` for you. + * This is, basically, `express-async-handler`: https://github.com/Abazhenov/express-async-handler/blob/master/index.js + * + * Example: + * ``` + * // this + * function (req, res, next) { + * service.create(req.body) + * .then(record => res.status(201).send(record)) + * .catch(next); + * } + * + * // turns into + * asyncHandler(async function(req, res) { + * const todo = await service.create(req.body); + * res.status(201).send(todo); + * }) + * ``` + */ +export const asyncHandler = < + S extends RequestSchema, + H extends RequestHandler +>( + handler: H +): RequestHandler => { + return function (req, res, next) { + const result = handler(req, res, next); + return Promise.resolve(result).catch(next); + }; +}; diff --git a/node-cli/assets/express/example-app/src/utils/api/index.ts b/node-cli/assets/express/example-app/src/utils/api/index.ts new file mode 100644 index 00000000..56860de4 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/index.ts @@ -0,0 +1,3 @@ +export * from './handler'; +export * from './route'; +export * from './response'; diff --git a/node-cli/assets/express/example-app/src/utils/api/response.test.ts b/node-cli/assets/express/example-app/src/utils/api/response.test.ts new file mode 100644 index 00000000..29db8e03 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/response.test.ts @@ -0,0 +1,52 @@ +import { ZodString, ZodObject } from 'zod'; +import { response } from './response'; + +describe('response', () => { + describe('NoContent', () => { + it('should be an OpenAPI response without content', () => { + const noContent = response.NoContent; + expect(noContent).toHaveProperty('description'); + expect(noContent).not.toHaveProperty('content'); + }); + }); + + describe('NotFound', () => { + it('should return a zod schema for an error', () => { + const schema = response.NotFound; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error' }; + expect(schema.parse(error)).toEqual(error); + }); + }); + + describe('Conflict', () => { + it('should return a zod schema for an error', () => { + const schema = response.Conflict; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error' }; + expect(schema.parse(error)).toEqual(error); + }); + }); + + describe('UnprocessableEntity', () => { + it('should return a zod schema for an error', () => { + const schema = response.UnprocessableEntity; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error', errors: [] }; + expect(schema.parse(error)).toEqual(error); + }); + }); + + describe('Unauthorized', () => { + it('should return a zod schema for an error', () => { + const schema = response.Unauthorized; + expect(schema).toBeInstanceOf(ZodObject); + expect(schema.shape.message).toBeInstanceOf(ZodString); + const error = { message: 'error' }; + expect(schema.parse(error)).toEqual(error); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/api/response.ts b/node-cli/assets/express/example-app/src/utils/api/response.ts new file mode 100644 index 00000000..eaea92d4 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/response.ts @@ -0,0 +1,23 @@ +import z from 'zod'; +import httpStatuses from 'statuses'; + +const error = (message: string) => + z.object({ message: z.string().openapi({ example: message }) }); + +const zodErrorIssue = z.object({ + code: z.string().openapi({ example: 'invalid_type' }), + expected: z.string().openapi({ example: 'string' }), + received: z.string().openapi({ example: 'number' }), + path: z.array(z.string()).openapi({ example: ['address', 'zip'] }), + message: z.string().openapi({ example: 'Expected string, received number' }), +}); + +export const response = { + NoContent: { description: httpStatuses.message[204] as string }, + Unauthorized: error('Unauthorized').openapi({ ref: 'UnauthorizedError' }), + NotFound: error('Record not found').openapi({ ref: 'NotFoundError' }), + Conflict: error('Record already exists').openapi({ ref: 'ConflictError' }), + UnprocessableEntity: error('Invalid input') + .extend({ errors: z.array(zodErrorIssue) }) + .openapi({ ref: 'UnprocessableEntityError' }), +}; diff --git a/node-cli/assets/express/example-app/src/utils/api/route.test.ts b/node-cli/assets/express/example-app/src/utils/api/route.test.ts new file mode 100644 index 00000000..537edf97 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/route.test.ts @@ -0,0 +1,40 @@ +import { defineRoute, prefixRoute, prefixRoutes } from './route'; + +describe('prefixRoute', () => { + it("should prefix the route's path", () => { + expect(prefixRoute('/users', { path: '/create' }).path).toBe( + '/users/create' + ); + }); +}); + +describe('prefixRoutes', () => { + it("should prefix the routes' path", () => { + const routes = [{ path: '/create' }, { path: '/update' }]; + + expect(prefixRoutes('/users', routes)).toEqual([ + { path: '/users/create' }, + { path: '/users/update' }, + ]); + }); +}); + +describe('defineRoute', () => { + it('should return an object with a attachHandler function', () => { + const routeDefinition = {}; + const result = defineRoute(routeDefinition as never); + expect(result).toHaveProperty('attachHandler'); + expect(result.attachHandler).toBeInstanceOf(Function); + }); + + describe('attachHandler', () => { + it('should add a handler property to the route definition', () => { + const routeDefinition = {}; + const handler = () => undefined; + const route = defineRoute(routeDefinition as never).attachHandler( + handler + ); + expect(route.handler).toBe(handler); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/api/route.ts b/node-cli/assets/express/example-app/src/utils/api/route.ts new file mode 100644 index 00000000..afea387b --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/api/route.ts @@ -0,0 +1,43 @@ +import { ZodOpenApiResponseObject } from 'zod-openapi'; +import { RequestSchema, RequestHandler } from './handler'; +import { RequestHandler as ExpressRequestHandler } from 'express'; +import { AnyZodObject } from 'zod'; + +type Responses = { + [statusCode: string]: ZodOpenApiResponseObject | AnyZodObject; +}; + +export type RouteDefinition = { + operationId: string; + summary?: string; + description?: string; + tags?: string[]; + method: 'get' | 'put' | 'post' | 'delete' | 'patch'; + path: string; + authenticate?: boolean; + request?: S; + middleware?: Array>; + handler: RequestHandler; + responses: Responses; +}; + +export const prefixRoute = ( + prefix: string, + route: R +): R => ({ + ...route, + path: `${prefix}${route.path}`, +}); + +export const prefixRoutes = ( + prefix: string, + routes: R[] +): R[] => routes.map((route) => prefixRoute(prefix, route)); + +export const defineRoute = ( + route: Omit, 'handler'> +) => ({ + attachHandler: ( + handler: RouteDefinition['handler'] + ): RouteDefinition => ({ ...route, handler }), +}); diff --git a/node-cli/assets/express/example-app/src/utils/environment.test.ts b/node-cli/assets/express/example-app/src/utils/environment.test.ts new file mode 100644 index 00000000..fa56f67c --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/environment.test.ts @@ -0,0 +1,7 @@ +import { environmentSchema } from './environment'; + +describe('envSchema', () => { + it('should be defined', () => { + expect(environmentSchema).toBeDefined(); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/environment.ts b/node-cli/assets/express/example-app/src/utils/environment.ts new file mode 100644 index 00000000..23e5d0ff --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/environment.ts @@ -0,0 +1,31 @@ +import { z } from 'zod'; + +const string = z.string().trim(); +const port = z.coerce.number().int().gte(1024).lte(65535); +const expiration = z.coerce.number().int().gte(1000).or(z.string()); +const secret = z.string().trim().min(10); + +export const environmentSchema = z.object({ + // Node + NODE_ENV: z.enum(['development', 'test', 'production']), + + // HTTP + PORT: port, + + REQUEST_LOGGING: z.enum(['true', 'false']).transform((value) => value === 'true'), + ERROR_LOGGING: z.enum(['true', 'false']).transform((value) => value === 'true'), + + // PostgreSQL + // TODO: this limits your options, should be revisited + PGHOST: string, + PGPORT: port, + PGUSER: string, + PGPASSWORD: string, + PGDATABASE: string, + + // JWT + JWT_SECRET: secret, + JWT_EXPIRATION: expiration, +}); + +export type Environment = Readonly>; diff --git a/node-cli/assets/express/example-app/src/utils/error.test.ts b/node-cli/assets/express/example-app/src/utils/error.test.ts new file mode 100644 index 00000000..562fc526 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/error.test.ts @@ -0,0 +1,59 @@ +import { ErrorMapping, mapError, rethrowError } from './error'; + +describe('mapError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const newError = new TypeError('type-error'); + + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return newError; + }, + }; + + it('should return the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(mapError(mapping)(error)).toBe(error); + }); + + it('should return the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(mapError(mapping)(error)).toBe(newError); + }); + }); +}); + +describe('rethrowError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return new TypeError('type-error'); + }, + }; + + it('should rethrow the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(() => rethrowError(mapping)(error)).toThrow(error); + }); + + it('should rethrow the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(() => rethrowError(mapping)(error)).toThrow( + new TypeError('type-error') + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/error.ts b/node-cli/assets/express/example-app/src/utils/error.ts new file mode 100644 index 00000000..df376a09 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/error.ts @@ -0,0 +1,28 @@ +type IsErrorPredicate = (error: unknown) => boolean; + +type ErrorFactory = () => Error; + +export interface ErrorMapping { + isError: IsErrorPredicate; + newError: ErrorFactory; +} + +export const mapError = function (...mappings: ErrorMapping[]) { + return function (err: Error) { + for (const { isError, newError } of mappings) { + if (isError(err)) { + return newError(); + } + } + + return err; + }; +}; + +export const rethrowError = function (...mappings: ErrorMapping[]) { + const errorMapper = mapError(...mappings); + + return function (err: Error) { + throw errorMapper(err); + }; +}; diff --git a/node-cli/assets/express/example-app/src/utils/openapi.test.ts b/node-cli/assets/express/example-app/src/utils/openapi.test.ts new file mode 100644 index 00000000..7ce68e8e --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/openapi.test.ts @@ -0,0 +1,178 @@ +import z from 'zod'; +import { generateDocument, reformatPathParams } from './openapi'; +import { response, RouteDefinition } from '@utils/api'; +import { ZodOpenApiRequestBodyObject } from 'zod-openapi'; + +describe('reformatPathParams', () => { + it('should change the format of the path params', () => { + expect(reformatPathParams('/users/:userId/posts/:postId')).toBe( + '/users/{userId}/posts/{postId}' + ); + }); +}); + +describe('generateDocument', () => { + const route: RouteDefinition = { + operationId: 'route-id', + summary: 'Does something', + description: 'It does somehting', + tags: ['tag'], + method: 'post', + path: '/users/:id', + handler: (_req, res) => { + res.send('OK'); + }, + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + name: { + type: 'string', + example: 'Laundry', + }, + }, + required: ['name'], + }, + }, + }, + }, + }, + }; + + it('should define bearer token authentication', () => { + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [], + }); + + expect(document.components?.securitySchemes?.bearerAuth).toEqual({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + }); + }); + + describe('when the route requires authentication', () => { + const routeWithAuth: RouteDefinition = { + ...route, + authenticate: true, + }; + + it('should add security definitions', () => { + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithAuth], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect(definition?.security).toEqual([ + { + bearerAuth: [], + }, + ]); + }); + }); + + describe('when a route has responses schemas', () => { + it('should not try to convert non-zod response schemas', () => { + const routeWithJsonSchemaResponse: RouteDefinition = { + ...route, + responses: { + 204: response.NoContent, + }, + }; + + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithJsonSchemaResponse], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect(definition?.responses['204']).toEqual({ + description: 'No Content', + }); + }); + + it('should map zod schemas to application/json content schemas', () => { + const routeWithJsonSchemaResponse: RouteDefinition = { + ...route, + request: { + body: z.object({ + id: z.number().int(), + name: z.string(), + }), + }, + responses: { + 200: z.object({ + id: z.number().int(), + name: z.string(), + }), + }, + }; + + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithJsonSchemaResponse], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect( + (definition?.requestBody as ZodOpenApiRequestBodyObject)['content'] + ).toHaveProperty('application/json'); + expect(definition?.responses['200'].content).toHaveProperty( + 'application/json' + ); + }); + + it('should provide a default description when one is not available for the status code', () => { + const routeWithDefaultDescription: RouteDefinition = { + ...route, + responses: { + 1234: z.object({}), + }, + }; + + const document = generateDocument({ + version: '3.0.3', + info: { + title: 'My API', + version: '1.0.0', + }, + routes: [routeWithDefaultDescription], + }); + + const definition = + document?.paths?.[reformatPathParams(route.path)][route.method]; + + expect(definition?.responses['1234'].description).toBe( + 'Unknown response code' + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/openapi.ts b/node-cli/assets/express/example-app/src/utils/openapi.ts new file mode 100644 index 00000000..ce3a3f33 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/openapi.ts @@ -0,0 +1,107 @@ +import { ZodType } from 'zod'; +import { + ZodOpenApiVersion, + createDocument, + ZodOpenApiObject, + ZodOpenApiPathsObject, +} from 'zod-openapi'; +import { RouteDefinition } from '@utils/api'; +import httpStatuses from 'statuses'; + +/** + * Changes the path param placeholder syntax from `:param` to `{param}` + */ +export const reformatPathParams = (path: string) => + path.replaceAll(/:[a-zA-Z]+/g, (match) => `{${match.substring(1)}}`); + +/** + * Maps our route definitions to OpenAPI path definitions + */ +export const mapRouteToPath = ({ + request, + responses, + ...route +}: RouteDefinition) => ({ + operationId: route.operationId, + summary: route.summary, + description: route.description, + tags: route.tags, + ...(route.authenticate && { + security: [ + { + bearerAuth: [], + }, + ], + }), + ...(request && { + requestParams: { + path: request.params, + query: request.query, + header: request.headers, + }, + }), + ...(request?.body && { + requestBody: { + content: { + 'application/json': { + schema: request.body, + }, + }, + }, + }), + responses: Object.fromEntries( + Object.entries(responses).map(([code, schema]) => [ + code, + schema instanceof ZodType + ? { + description: + httpStatuses.message[code as never as number] ?? + 'Unknown response code', + content: { 'application/json': { schema } }, + } + : schema, + ]) + ), +}); + +/** + * Maps our route definitions to an OpenAPI paths object definition + */ +export const mapRoutesToPaths = (routes: RouteDefinition[]) => + routes.reduce((paths, route) => { + const path = reformatPathParams(route.path); + // eslint-disable-next-line security/detect-object-injection + paths[path] ||= {}; + // eslint-disable-next-line security/detect-object-injection + paths[path][route.method] = mapRouteToPath(route); + return paths; + }, {} as ZodOpenApiPathsObject); + +/** + * Generates the OpenAPI document + */ +export const generateDocument = ({ + version, + info, + routes, +}: { + // 3.1.0 is not yet supported by our version of swagger-ui + version: Exclude; + info: ZodOpenApiObject['info']; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + routes: RouteDefinition[]; +}) => + createDocument({ + openapi: version, + info, + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + }, + }, + }, + paths: mapRoutesToPaths(routes), + }); diff --git a/node-cli/assets/express/example-app/src/utils/query/error.test.ts b/node-cli/assets/express/example-app/src/utils/query/error.test.ts new file mode 100644 index 00000000..da408b48 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/error.test.ts @@ -0,0 +1,47 @@ +import { RecordNotFoundError } from '@database/errors'; +import { definedOrNotFound, updatedOrNotFound } from './error'; + +describe('definedOrNotFound', () => { + it('should return a function', () => { + expect(typeof definedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the value it is called with when the value is not undefined', () => { + expect(definedOrNotFound('message')(null)).toBe(null); + expect(definedOrNotFound('message')(false)).toBe(false); + expect(definedOrNotFound('message')(0)).toBe(0); + expect(definedOrNotFound('message')('')).toBe(''); + const emptyArray: unknown[] = []; + expect(definedOrNotFound('message')(emptyArray)).toBe(emptyArray); + const emptyObject = {}; + expect(definedOrNotFound('message')(emptyObject)).toEqual(emptyObject); + }); + + it('should throw RecordNotFound when the value it is called with is undefined', () => { + expect(() => definedOrNotFound('message')(undefined)).toThrow( + new RecordNotFoundError('message') + ); + }); + }); +}); + +describe('updatedOrNotFound', () => { + it('should return a function', () => { + expect(typeof updatedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the number it is called with when the number is not zero', () => { + expect(updatedOrNotFound('message')(-1)).toBe(-1); + expect(updatedOrNotFound('message')(1)).toBe(1); + expect(updatedOrNotFound('message')(NaN)).toEqual(NaN); + }); + + it('should throw RecordNotFound when the value it is called with is zero', () => { + expect(() => updatedOrNotFound('message')(0)).toThrow( + new RecordNotFoundError('message') + ); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/query/error.ts b/node-cli/assets/express/example-app/src/utils/query/error.ts new file mode 100644 index 00000000..b903287e --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/error.ts @@ -0,0 +1,25 @@ +import { RecordNotFoundError } from '@database/errors'; + +const definedOrThrow = (errorFactory: () => Error) => { + return (result: T | undefined): T => { + if (result === undefined) { + throw errorFactory(); + } + return result; + }; +}; + +const updatedOrThrow = (errorFactory: () => Error) => { + return (result: number): number => { + if (result === 0) { + throw errorFactory(); + } + return result; + }; +}; + +export const definedOrNotFound = (message: string) => + definedOrThrow(() => new RecordNotFoundError(message)); + +export const updatedOrNotFound = (message: string) => + updatedOrThrow(() => new RecordNotFoundError(message)); diff --git a/node-cli/assets/express/example-app/src/utils/query/filtration.ts b/node-cli/assets/express/example-app/src/utils/query/filtration.ts new file mode 100644 index 00000000..849cae26 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/filtration.ts @@ -0,0 +1,51 @@ +import { Knex } from 'knex'; + +/** + * A Filter is a function that accepts a QueryBuilder for a given entity and a value, + * applies the filter to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type FilterByName = Filter, Todo['name']>; + * + * const filterByName: FilterByName = (qb, name) => qb.where({ name }); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Filter = ( + qb: Knex.QueryBuilder, + value: Value +) => Knex.QueryBuilder; + +/** + * A FilerMap is a map is a mapping from query param names to filters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all query parameters have a corresponding filter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * // This would typically be obtained via z.infer + * interface ListTodosFilters { + * id?: number; + * name?: string; + * }; + * + * const listTodosFilterMap: FilterMap, ListTodosFilters> = { + * id: (qb, id) => qb.where({ id }), + * name: (qb, name) => qb.whereILike('name', `%${name}%`), + * }; + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type FilterMap = { + // Since query params are usually optional, + // we loop through the query params removing their optionality via `-?`, + // then map them to a filter of their value excluding undefined. + [K in keyof Filters]-?: Filter>; +}; diff --git a/node-cli/assets/express/example-app/src/utils/query/index.ts b/node-cli/assets/express/example-app/src/utils/query/index.ts new file mode 100644 index 00000000..794eb80f --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/index.ts @@ -0,0 +1,5 @@ +export * from './error'; +export * from './filtration'; +export * from './pagination'; +export * from './result'; +export * from './sorting'; diff --git a/node-cli/assets/express/example-app/src/utils/query/pagination.test.ts b/node-cli/assets/express/example-app/src/utils/query/pagination.test.ts new file mode 100644 index 00000000..a9a54987 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/pagination.test.ts @@ -0,0 +1,45 @@ +import z, { ZodArray, ZodObject, ZodNumber } from 'zod'; +import { paginated, extractPagination, paginationDefaults } from './pagination'; + +describe('paginated', () => { + it('should return a new schema that describes a paginated resuls', () => { + const schema = z.number(); + const paginatedSchema = paginated(schema); + + expect(paginatedSchema.shape).toEqual( + expect.objectContaining({ + data: expect.any(ZodArray), + meta: expect.any(ZodObject), + }) + ); + + expect(paginatedSchema.shape.data.element).toBe(schema); + + expect(paginatedSchema.shape.meta.shape).toEqual( + expect.objectContaining({ + total: expect.any(ZodNumber), + page: expect.any(ZodNumber), + items: expect.any(ZodNumber), + }) + ); + }); +}); + +describe('extractPagination', () => { + it('should return the provided pagination', () => { + const pagination = { page: 1, items: 10 }; + expect(extractPagination(pagination)).toEqual(pagination); + }); + + it('should supply the defaults for any missing pagination properties', () => { + expect(extractPagination()).toEqual(paginationDefaults); + expect(extractPagination({ page: 1 })).toEqual({ + ...paginationDefaults, + page: 1, + }); + expect(extractPagination({ items: 10 })).toEqual({ + ...paginationDefaults, + items: 10, + }); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/query/pagination.ts b/node-cli/assets/express/example-app/src/utils/query/pagination.ts new file mode 100644 index 00000000..0b1e0e2e --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/pagination.ts @@ -0,0 +1,35 @@ +import { z, ZodType } from 'zod'; + +// TODO: move to a config or smth +export const paginationDefaults = { + page: 1, + items: 20, +}; + +export const pagination = z + .object({ + page: z.coerce.number().int().positive(), + items: z.coerce.number().int().positive(), + }) + .partial(); + +const paginationMeta = pagination.required().extend({ + total: z.number().int().nonnegative(), +}); + +export type Pagination = z.infer; +export type PaginationMeta = z.infer; + +export interface Paginated { + data: Entity[]; + meta: PaginationMeta; +} + +export const paginated = >(schema: Schema) => + z.object({ + data: z.array(schema), + meta: paginationMeta, + }); + +export const extractPagination = (pagination?: Pagination) => + Object.assign({}, paginationDefaults, pagination); diff --git a/node-cli/assets/express/example-app/src/utils/query/result.test.ts b/node-cli/assets/express/example-app/src/utils/query/result.test.ts new file mode 100644 index 00000000..6cf8a427 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/result.test.ts @@ -0,0 +1,11 @@ +import { parseCount } from './result'; + +describe('parseCount', () => { + it('should extract and parse the count when the count is a string', () => { + expect(parseCount([{ count: '10' }])).toBe(10); + }); + + it('should extract the count when the count is a number', () => { + expect(parseCount([{ count: 10 }])).toBe(10); + }); +}); diff --git a/node-cli/assets/express/example-app/src/utils/query/result.ts b/node-cli/assets/express/example-app/src/utils/query/result.ts new file mode 100644 index 00000000..2466eb02 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/result.ts @@ -0,0 +1,4 @@ +export type CountResult = Array<{ count?: string | number }>; + +export const parseCount = ([{ count }]: CountResult): number => + typeof count === 'string' ? parseInt(count) : (count as number); diff --git a/node-cli/assets/express/example-app/src/utils/query/sorting.ts b/node-cli/assets/express/example-app/src/utils/query/sorting.ts new file mode 100644 index 00000000..03a9680a --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/query/sorting.ts @@ -0,0 +1,65 @@ +import { Knex } from 'knex'; +import z, { ZodEnum } from 'zod'; + +export const sortOrder = z.enum(['asc', 'desc']); + +export const sorts = >( + schema: Schema +) => + z.array( + z.object({ + column: schema, + order: sortOrder.optional(), + }) + ); + +export type SortOrder = z.infer; + +export interface Sort { + column: SortColumn; + order?: SortOrder; +} + +/** + * A Sorter is a function that accepts a QueryBuilder for a given entity and a sort order, + * applies the sorting to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type SortByName = Sorter>; + * + * const sortByName: SortByName = (qb, order) => qb.orderBy('name', order); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Sorter = ( + qb: Knex.QueryBuilder, + order?: SortOrder +) => Knex.QueryBuilder; + +/** + * A SorterMap is a map is a mapping from query param names to sorters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all sort parameters have a corresponding sorter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type ListTodoSortColumn = 'name' | 'createdAt'; + * + * const listTodosSorterMap: SorterMap, ListTodoSortColumn> = { + * name: (qb, order) => qb.orderBy('name', order), + * createdAt: (qb, order) => qb.orderBy('createdAt', order), + * }; + * ``` + */ +export type SorterMap< + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + SortColumn extends string +> = Record>; diff --git a/node-cli/assets/express/example-app/src/utils/types.ts b/node-cli/assets/express/example-app/src/utils/types.ts new file mode 100644 index 00000000..1e2f37af --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/types.ts @@ -0,0 +1,7 @@ +export type IsNullable = null extends T ? K : never; + +export type NullableKeys = { [K in keyof T]: IsNullable }[keyof T]; + +/** `Partial` just for the nullable keys */ +export type NullableKeysPartial = Omit> & + Partial>>; diff --git a/node-cli/assets/express/example-app/src/utils/validation/attribute-sets.ts b/node-cli/assets/express/example-app/src/utils/validation/attribute-sets.ts new file mode 100644 index 00000000..0b552a25 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/validation/attribute-sets.ts @@ -0,0 +1,15 @@ +import z from 'zod'; +import { id as idSchema, timestamp } from './attributes'; + +export const withId = z.object({ + id: idSchema, +}); + +export type WithId = z.infer; + +export const withTimestamps = z.object({ + createdAt: timestamp, + updatedAt: timestamp, +}); + +export type WithTimestamps = z.infer; diff --git a/node-cli/assets/express/example-app/src/utils/validation/attributes.ts b/node-cli/assets/express/example-app/src/utils/validation/attributes.ts new file mode 100644 index 00000000..e0fd83d2 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/validation/attributes.ts @@ -0,0 +1,22 @@ +import { z } from 'zod'; + +export const id = z.number().int().positive().openapi({ example: 1 }); + +export const email = z + .string() + .trim() + .toLowerCase() + .max(255) + .email() + .openapi({ example: 'john@mail.com' }); + +export const password = z + .string() + .min(6) + .max(255) + .openapi({ example: 'MyS3cr37Pass' }); + +export const timestamp = z + .string() + .datetime() + .openapi({ example: '2023-02-28T14:39:24.086Z' }); diff --git a/node-cli/assets/express/example-app/src/utils/validation/index.ts b/node-cli/assets/express/example-app/src/utils/validation/index.ts new file mode 100644 index 00000000..3b3a6a23 --- /dev/null +++ b/node-cli/assets/express/example-app/src/utils/validation/index.ts @@ -0,0 +1,2 @@ +export * from './attribute-sets'; +export * from './attributes'; diff --git a/node-cli/assets/express/example-app/test/auth/login.spec-e2e.ts b/node-cli/assets/express/example-app/test/auth/login.spec-e2e.ts new file mode 100644 index 00000000..bc3bbb13 --- /dev/null +++ b/node-cli/assets/express/example-app/test/auth/login.spec-e2e.ts @@ -0,0 +1,72 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + InvalidCredentials, + UnprocessableEntity, +} from '../utils'; +import { Knex } from 'knex'; +import { create } from '@database'; + +describe('POST /auth/login', () => { + let app: Express.Application; + let destroy: () => Promise; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + afterAll(async () => { + await destroy(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + }); + + describe('given the email and password are valid', () => { + it('should login the user and return a jwt token', async () => { + const res = await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'pass@ord' }) + .expect(200); + expect(typeof res.body.idToken).toBe('string'); + }); + + describe('when there are empty credentials', () => { + it('should return 422', async () => { + await request(app) + .post('/auth/login') + .send({}) + .expect('content-type', /json/) + .expect(expectError(UnprocessableEntity)); + }); + }); + + describe('when the email does not exist in db', () => { + it('should return 422', async () => { + await request(app) + .post('/auth/login') + .send({ email: 'fake@email.com', password: 'pass@ord' }) + .expect('content-type', /json/) + .expect(expectError(InvalidCredentials)); + }); + }); + }); + + describe('when the password does not match in db', () => { + it('should return 422', async () => { + await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'wrong password' }) + .expect('content-type', /json/) + .expect(expectError(InvalidCredentials)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/test/auth/register.spec-e2e.ts b/node-cli/assets/express/example-app/test/auth/register.spec-e2e.ts new file mode 100644 index 00000000..cb1558d4 --- /dev/null +++ b/node-cli/assets/express/example-app/test/auth/register.spec-e2e.ts @@ -0,0 +1,60 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + UnprocessableEntity, + UserConflict, +} from '../utils'; +import { create } from '@database'; +import { Knex } from 'knex'; + +describe('POST /auth/register', () => { + let app: Express.Application; + let destroy: () => Promise; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + afterAll(async () => { + await destroy(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + }); + + it('should create a new user and return a jwt token', async () => { + const res = await request(app) + .post('/auth/register') + .send({ email: 'email@email.com', password: 'pass@ord' }) + .expect('content-type', /json/) + .expect(200); + expect(typeof res.body.idToken).toBe('string'); + }); + + it('should return 409 error when there is an existing user', async () => { + await request(app) + .post('/auth/register') + .send({ email: 'hello@email.com', password: 'pass@ord' }) + .expect('content-type', /json/) + .expect(expectError(UserConflict)); + }); + + it('should return 422 error when there is invalid payload', async () => { + await request(app) + .post('/auth/register') + .send({ + email: 'test', + password: 'test', + }) + .expect('content-type', /json/) + .expect(expectError(UnprocessableEntity)); + }); +}); diff --git a/node-cli/assets/express/example-app/test/healthchecks/live.spec-e2e.ts b/node-cli/assets/express/example-app/test/healthchecks/live.spec-e2e.ts new file mode 100644 index 00000000..ed2bf694 --- /dev/null +++ b/node-cli/assets/express/example-app/test/healthchecks/live.spec-e2e.ts @@ -0,0 +1,21 @@ +import request from 'supertest'; +import { create as createApp } from '../utils/app'; + +describe('GET /healthz/live', () => { + let app: Express.Application; + let destroy: () => Promise; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return 200', async () => { + await request(app).get('/healthz/live').expect(200); + }); +}); diff --git a/node-cli/assets/express/example-app/test/healthchecks/ready.spec-e2e.ts b/node-cli/assets/express/example-app/test/healthchecks/ready.spec-e2e.ts new file mode 100644 index 00000000..3b48efb5 --- /dev/null +++ b/node-cli/assets/express/example-app/test/healthchecks/ready.spec-e2e.ts @@ -0,0 +1,21 @@ +import request from 'supertest'; +import { create as createApp } from '../utils/app'; + +describe('GET /healthz/ready', () => { + let app: Express.Application; + let destroy: () => Promise; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return 200', async () => { + await request(app).get('/healthz/ready').expect(200); + }); +}); diff --git a/node-cli/assets/express/example-app/test/jest-e2e.config.js b/node-cli/assets/express/example-app/test/jest-e2e.config.js new file mode 100644 index 00000000..1f7737d8 --- /dev/null +++ b/node-cli/assets/express/example-app/test/jest-e2e.config.js @@ -0,0 +1,70 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + // run test via Nodejs + testEnvironment: 'node', + // needed for TypeScript + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + // root dir is the app root + rootDir: '..', + // match anything that is spec-e2e or test-e2e with a js, jsx, ts and tsx extension + testMatch: [ + "**/?(*.)+(spec|test)-e2e.[tj]s?(x)" + ], + // does some setup before each test file + setupFiles: [ + '/test/jest.setup.ts' + ], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, + // disable unit test manual mocks + modulePathIgnorePatterns: [ + '/__mocks__', + '/src/.*/__mocks__' + ], + // change coverage directory name so that it doesn't overlap with unit test coverage + coverageDirectory: '/coverage-e2e', + // coverage is collected from files under src/ + collectCoverageFrom: [ + '/src/api/**/*.[tj]s?(x)', + ], + // and from those files ignore + coveragePathIgnorePatterns: [ + // entry points + '/src/index.[tj]sx?$', + '/src/app.[tj]sx?$', + // extensions + '/src/extensions', + // module indexes + '/.*/index.[tj]sx?$', + // unit tests + '(spec|test).[tj]sx?$', + // type definitions + '/src/@types/', + // database migrations + '/src/modules/database/migrations', + // utility code + '/src/common', + '/src/modules/database/utils', + // middleware + '/src/api/middleware' + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, +}; diff --git a/node-cli/assets/express/example-app/test/jest.setup.ts b/node-cli/assets/express/example-app/test/jest.setup.ts new file mode 100644 index 00000000..e34878f0 --- /dev/null +++ b/node-cli/assets/express/example-app/test/jest.setup.ts @@ -0,0 +1,7 @@ +import '@extensions/zod/register'; +import '@extensions/knex/register'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/node-cli/assets/express/example-app/test/todos/create.spec-e2e.ts b/node-cli/assets/express/example-app/test/todos/create.spec-e2e.ts new file mode 100644 index 00000000..e769380f --- /dev/null +++ b/node-cli/assets/express/example-app/test/todos/create.spec-e2e.ts @@ -0,0 +1,72 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + getTodoPayload, + Unauthorized, + UnprocessableEntity +} from '../utils'; +import { JwtTokens } from '@api/auth'; +import { Knex } from 'knex'; +import { create } from '@database'; + +describe('POST /v1/todos', () => { + let app: Express.Application; + let destroy: () => Promise; + let jwtTokens: JwtTokens; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + const res = await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'pass@ord' }); + + jwtTokens = res.body; + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return the created todo', async () => { + const todoPayload = getTodoPayload(); + + const res = await request(app) + .post('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .send(todoPayload) + + expect(res.body.name).toEqual(todoPayload.name); + expect(res.body.note).toEqual(todoPayload.note); + expect(res.body.completed).toEqual(todoPayload.completed); + }); + + it('given an empty payload, should return 422 error', async () => { + await request(app) + .post('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .send({}) + .expect(expectError(UnprocessableEntity)); + }); + + it('should return 401 error when the user is not authenticated', async () => { + await request(app) + .post('/v1/todos') + .set('Content-Type', 'application/json') + .send(getTodoPayload()) + .expect(expectError(Unauthorized)); + }); +}); diff --git a/node-cli/assets/express/example-app/test/todos/delete.spec-e2e.ts b/node-cli/assets/express/example-app/test/todos/delete.spec-e2e.ts new file mode 100644 index 00000000..7e8daa21 --- /dev/null +++ b/node-cli/assets/express/example-app/test/todos/delete.spec-e2e.ts @@ -0,0 +1,64 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + Unauthorized, + TodoNotFound +} from '../utils'; +import { JwtTokens } from '@api/auth'; +import { Knex } from 'knex'; +import { create } from '@database'; + +describe('DELETE /v1/todos/:id', () => { + let app: Express.Application; + let destroy: () => Promise; + let jwtTokens: JwtTokens; + const todoId = 1; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + const res = await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'pass@ord' }); + + jwtTokens = res.body; + }); + + afterAll(async () => { + await destroy(); + }); + + it('should return 204 when todo is deleted', async () => { + await request(app) + .delete(`/v1/todos/${todoId}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .expect(204); + }); + + it('should return 404 when given not existing todo id', async () => { + await request(app) + .delete(`/v1/todos/${Date.now()}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .set('Content-Type', 'application/json') + .expect(expectError(TodoNotFound)); + }); + + it('should return 401 error when user is not authenticated', async () => { + await request(app) + .delete(`/v1/todos/${todoId}`) + .set('Content-Type', 'application/json') + .expect(expectError(Unauthorized)); + }); +}); diff --git a/node-cli/assets/express/example-app/test/todos/get.spec-e2e.ts b/node-cli/assets/express/example-app/test/todos/get.spec-e2e.ts new file mode 100644 index 00000000..7875216e --- /dev/null +++ b/node-cli/assets/express/example-app/test/todos/get.spec-e2e.ts @@ -0,0 +1,91 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + TodoNotFound, + Unauthorized, + UnprocessableEntity, +} from '../utils'; +import { JwtTokens } from '@api/auth'; +import { Todo } from '@api/todos'; +import { Knex } from 'knex'; +import { create } from '@database'; + +describe('GET /v1/todos/:id', () => { + let app: Express.Application; + let destroy: () => Promise; + let jwtTokens: JwtTokens; + let dbClient: Knex; + const todo: Partial = { + id: 1, + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + const res = await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'pass@ord' }); + + jwtTokens = res.body; + }); + + afterAll(async () => { + await destroy(); + }); + + describe('when user is authenticated', () => { + describe('given todo id in the query', () => { + it('should return the todo', async () => { + const res = await request(app) + .get(`/v1/todos/${todo.id}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.id).toEqual(todo.id); + expect(res.body.name).toEqual(todo.name); + expect(res.body.note).toEqual(todo.note); + expect(res.body.completed).toEqual(todo.completed); + }); + }); + + describe('given not existing todo id in the query', () => { + it('should return 404', async () => { + await request(app) + .get(`/v1/todos/${Date.now()}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(expectError(TodoNotFound)); + }); + }); + + describe('given a text id in the query', () => { + it('should return 422 error', async () => { + await request(app) + .get(`/v1/todos/test`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(expectError(UnprocessableEntity)); + }); + }); + }); + + describe('when user is not authenticated', () => { + it('should return 401 error', async () => { + await request(app) + .get(`/v1/todos/${todo.id}`) + .expect(expectError(Unauthorized)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/test/todos/list.spec-e2e.ts b/node-cli/assets/express/example-app/test/todos/list.spec-e2e.ts new file mode 100644 index 00000000..a6bfff36 --- /dev/null +++ b/node-cli/assets/express/example-app/test/todos/list.spec-e2e.ts @@ -0,0 +1,211 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + getTodoPayload, + sortByField, + Unauthorized, +} from '../utils'; +import { JwtTokens } from '@api/auth'; +import { Knex } from 'knex'; +import { create } from '@database'; + +describe('GET /v1/todos', () => { + let app: Express.Application; + let destroy: () => Promise; + let jwtTokens: JwtTokens; + let dbClient: Knex; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + const res = await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'pass@ord' }); + + jwtTokens = res.body; + }); + + afterAll(async () => { + await destroy(); + }); + + describe('when user is authenticated', () => { + describe('when no filters are applied', () => { + it('should return whole todos list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(3); + }); + }); + + describe('when completed=true filter is applied', () => { + it('should return completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?filters[completed]=true') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(1); + }); + }); + + describe('when completed=false filter is applied', () => { + it('should return not completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?filters[completed]=false') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(2); + }); + }); + + describe('when name = "Laundry" filter is applied', () => { + it('should return todo list with matched results', async () => { + const res = await request(app) + .get('/v1/todos?filters[name]=Laundry') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(3); + }); + }); + + describe('when pagination items is applied', () => { + it('should return not completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?pagination[items]=2') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(2); + }); + }); + + describe('when pagination page is applied', () => { + it('should return not completed todo list', async () => { + const res = await request(app) + .get('/v1/todos?pagination[page]=2&pagination[items]=3') + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(200); + + expect(res.body.data.length).toEqual(0); + expect(res.body.meta.total).toEqual(3); + }); + }); + + describe('when sort by name desc', () => { + it('should return desc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=name&sorts[0][order]=desc') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'name', 'desc') + ); + }); + }); + + describe('when sort by name asc', () => { + it('should return asc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=name&sorts[0][order]=asc') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'name', 'asc') + ); + }); + }); + + describe('when sort by createdAt desc', () => { + it('should return desc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=createdAt&sorts[0][order]=desc') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'createdAt', 'desc') + ); + }); + }); + + describe('when sort by createdAt asc', () => { + it('should return asc ordered todo list', async () => { + const res = await request(app) + .get('/v1/todos') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + const sortedRes = await request(app) + .get('/v1/todos?sorts[0][column]=createdAt') + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(sortedRes.headers['content-type']).toMatch(/json/); + expect(sortedRes.status).toEqual(200); + expect(sortedRes.body.meta.total).toEqual(res.body.meta.total); + expect(sortedRes.body.data).toStrictEqual( + sortByField(res.body.data, 'createdAt', 'asc') + ); + }); + }); + + describe('when all filters are applied', () => { + it('should return filtered todo list', async () => { + const res = await request(app) + .get( + '/v1/todos?filters[name]=Laundry&sorts[0][column]=name&sorts[0][order]=desc&pagination[page]=1&pagination[items]=2' + ) + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(res.headers['content-type']).toMatch(/json/); + expect(res.status).toEqual(200); + expect(res.body.data.length).toEqual(2); + expect(res.body.meta.total).toEqual(3); + }); + }); + }); + + describe('when user is not authenticated', () => { + it('should return 401 error', async () => { + await request(app) + .get('/v1/todos') + .send(getTodoPayload()) + .expect('content-type', /json/) + .expect(expectError(Unauthorized)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/test/todos/update.spec-e2e.ts b/node-cli/assets/express/example-app/test/todos/update.spec-e2e.ts new file mode 100644 index 00000000..723ba20a --- /dev/null +++ b/node-cli/assets/express/example-app/test/todos/update.spec-e2e.ts @@ -0,0 +1,112 @@ +import request from 'supertest'; +import { + create as createApp, + expectError, + getTodoPayload, + TodoNotFound, + Unauthorized, + UnprocessableEntity, +} from '../utils'; +import { JwtTokens } from '@api/auth'; +import { Knex } from 'knex'; +import { create } from '@database'; + +describe('POST /v1/todos/:id', () => { + let app: Express.Application; + let destroy: () => Promise; + let jwtTokens: JwtTokens; + let dbClient: Knex; + const todoId = 1; + + beforeAll(() => { + const { app: _app, destroy: _destroy } = createApp(); + app = _app; + destroy = _destroy; + dbClient = create(); + }); + + beforeEach(async () => { + await dbClient.migrate.rollback(); + await dbClient.migrate.latest(); + await dbClient.seed.run(); + + const res = await request(app) + .post('/auth/login') + .send({ email: 'hello@email.com', password: 'pass@ord' }); + + jwtTokens = res.body; + }); + + afterAll(async () => { + await destroy(); + }); + + describe('when user is authenticated', () => { + describe('given the todo payload and id in the query', () => { + it('should return the updated todo', async () => { + const todoPayload = getTodoPayload(); + + const res = await request(app) + .patch(`/v1/todos/${todoId}`) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .send(todoPayload) + .expect(200); + + expect(res.body.name).toEqual(todoPayload.name); + expect(res.body.note).toEqual(todoPayload.note); + expect(res.body.completed).toEqual(todoPayload.completed); + }); + }); + + describe('given an empty payload and id in the query', () => { + it('should return the not updated todo', async () => { + const todo = await dbClient('todos').where({ id: todoId }).first(); + if (!todo) { + throw new Error('Todo not found'); + } + + const res = await request(app) + .patch(`/v1/todos/${todoId}`) + .send({}) + .set('Authorization', 'Bearer ' + jwtTokens.idToken); + + expect(res.status).toEqual(200); + expect(res.body.id).toEqual(todo.id); + expect(res.body.name).toEqual(todo.name); + expect(res.body.note).toEqual(todo.note); + expect(res.body.completed).toEqual(todo.completed); + expect(res.body.userId).toEqual(todo.userId); + }); + }); + + describe('given not existing todo id in the query', () => { + it('should return 404', async () => { + await request(app) + .patch(`/v1/todos/${Date.now()}`) + .send(getTodoPayload()) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect('content-type', /json/) + .expect(expectError(TodoNotFound)); + }); + }); + + describe('given a text id in the query', () => { + it('should return 422 error', async () => { + await request(app) + .patch(`/v1/todos/test`) + .send(getTodoPayload()) + .set('Authorization', 'Bearer ' + jwtTokens.idToken) + .expect(expectError(UnprocessableEntity)); + }); + }); + }); + + describe('when user is not authenticated', () => { + it('should return 401 error', async () => { + await request(app) + .patch(`/v1/todos/${todoId}`) + .send(getTodoPayload()) + .expect(expectError(Unauthorized)); + }); + }); +}); diff --git a/node-cli/assets/express/example-app/test/utils/app.ts b/node-cli/assets/express/example-app/test/utils/app.ts new file mode 100644 index 00000000..2c572c01 --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/app.ts @@ -0,0 +1,7 @@ +import { environmentSchema } from '@utils/environment'; +import { create as createApp } from '../../src/app'; + +export const create = () => { + const env = Object.freeze(environmentSchema.parse(process.env)); + return createApp(env); +}; diff --git a/node-cli/assets/express/example-app/test/utils/errors.ts b/node-cli/assets/express/example-app/test/utils/errors.ts new file mode 100644 index 00000000..25d445b3 --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/errors.ts @@ -0,0 +1,16 @@ +import { HttpException } from './http-exception'; + +export const Unauthorized = (message = 'No authorization token was found') => + new HttpException(401, message); + +export const UnprocessableEntity = (message = 'Bad Data') => + new HttpException(422, message); + +export const TodoNotFound = (message = 'To-Do not found') => + new HttpException(404, message); + +export const UserConflict = (message = 'User email already taken') => + new HttpException(409, message); + +export const InvalidCredentials = (message = 'Invalid email or password') => + new HttpException(422, message); diff --git a/node-cli/assets/express/example-app/test/utils/expect-error.ts b/node-cli/assets/express/example-app/test/utils/expect-error.ts new file mode 100644 index 00000000..c94debc6 --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/expect-error.ts @@ -0,0 +1,25 @@ +import { Response } from 'supertest'; +import { + InvalidCredentials, + TodoNotFound, + Unauthorized, + UnprocessableEntity, + UserConflict, +} from './errors'; +import { HttpException } from './http-exception'; + +const httpExceptionMap = { + [Unauthorized.name]: Unauthorized(), + [UnprocessableEntity.name]: UnprocessableEntity(), + [TodoNotFound.name]: TodoNotFound(), + [UserConflict.name]: UserConflict(), + [InvalidCredentials.name]: InvalidCredentials(), +}; + +export const expectError = + (ex: (message?: string) => HttpException) => (res: Response) => { + const error = httpExceptionMap[ex.name]; + expect(res.error).toBeDefined(); + expect(res.body.message).toEqual(error.message); + expect(res.status).toEqual(error.status); + }; diff --git a/node-cli/assets/express/example-app/test/utils/get-todo-payload.ts b/node-cli/assets/express/example-app/test/utils/get-todo-payload.ts new file mode 100644 index 00000000..7fda6f62 --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/get-todo-payload.ts @@ -0,0 +1,9 @@ +import { Todo } from '@api/todos'; + +export const getTodoPayload: ( + completed?: boolean +) => Pick = (completed) => ({ + name: 'Laundry' + Date.now(), + note: 'Buy detergent' + Date.now(), + completed: completed ?? false, +}); diff --git a/node-cli/assets/express/example-app/test/utils/http-exception.ts b/node-cli/assets/express/example-app/test/utils/http-exception.ts new file mode 100644 index 00000000..f90512cb --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/http-exception.ts @@ -0,0 +1,7 @@ +export class HttpException extends Error { + constructor(public status: number, public message: string) { + super(message); + this.status = status; + this.message = message; + } +} diff --git a/node-cli/assets/express/example-app/test/utils/index.ts b/node-cli/assets/express/example-app/test/utils/index.ts new file mode 100644 index 00000000..5ab69612 --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/index.ts @@ -0,0 +1,7 @@ +export * from './errors'; + +export { create } from './app'; +export { getTodoPayload } from './get-todo-payload'; +export { sortByField } from './sortby-field-todos'; +export { expectError } from './expect-error'; +export { HttpException } from './http-exception'; diff --git a/node-cli/assets/express/example-app/test/utils/sortby-field-todos.ts b/node-cli/assets/express/example-app/test/utils/sortby-field-todos.ts new file mode 100644 index 00000000..a2537c6e --- /dev/null +++ b/node-cli/assets/express/example-app/test/utils/sortby-field-todos.ts @@ -0,0 +1,19 @@ +import { Todo } from '@api/todos/entities'; +import { SortOrder } from '@utils/query'; + +type ListTodoSortColumn = 'name' | 'createdAt'; + +export const sortByField: ( + list: Todo[], + field: ListTodoSortColumn, + order?: SortOrder +) => Todo[] = (list, field, order) => { + return list.sort((a, b) => { + if (order === 'desc') { + // eslint-disable-next-line security/detect-object-injection + return b[field].localeCompare(a[field]); + } + // eslint-disable-next-line security/detect-object-injection + return a[field].localeCompare(b[field]); + }); +}; diff --git a/node-cli/assets/express/example-app/tsconfig.json b/node-cli/assets/express/example-app/tsconfig.json new file mode 100644 index 00000000..a4cca151 --- /dev/null +++ b/node-cli/assets/express/example-app/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "@tsconfig/recommended/tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "baseUrl": ".", + "moduleResolution": "node", + "resolveJsonModule": true, + "skipLibCheck": false, + "lib": ["ES2021"], + "paths": { + "@api/*": ["src/api/*"], + "@database": ["src/database"], + "@database/*": ["src/database/*"], + "@extensions/*": ["src/extensions/*"], + "@middleware": ["src/middleware"], + "@utils/*": ["src/utils/*"] + } + }, + "ts-node": { + "files": true, + "require": ["tsconfig-paths/register", "dotenv/config"] + } +} diff --git a/assets/express/js/.gitignore b/node-cli/assets/express/js/.gitignore similarity index 100% rename from assets/express/js/.gitignore rename to node-cli/assets/express/js/.gitignore diff --git a/assets/express/js/jest.config.js b/node-cli/assets/express/js/jest.config.js similarity index 100% rename from assets/express/js/jest.config.js rename to node-cli/assets/express/js/jest.config.js diff --git a/assets/express/js/src/app.js b/node-cli/assets/express/js/src/app.js similarity index 100% rename from assets/express/js/src/app.js rename to node-cli/assets/express/js/src/app.js diff --git a/assets/express/js/src/hello-world.js b/node-cli/assets/express/js/src/hello-world.js similarity index 100% rename from assets/express/js/src/hello-world.js rename to node-cli/assets/express/js/src/hello-world.js diff --git a/assets/express/js/src/hello-world.test.js b/node-cli/assets/express/js/src/hello-world.test.js similarity index 100% rename from assets/express/js/src/hello-world.test.js rename to node-cli/assets/express/js/src/hello-world.test.js diff --git a/assets/express/js/src/index.js b/node-cli/assets/express/js/src/index.js similarity index 100% rename from assets/express/js/src/index.js rename to node-cli/assets/express/js/src/index.js diff --git a/assets/express/js/test/hello-world.spec-e2e.js b/node-cli/assets/express/js/test/hello-world.spec-e2e.js similarity index 100% rename from assets/express/js/test/hello-world.spec-e2e.js rename to node-cli/assets/express/js/test/hello-world.spec-e2e.js diff --git a/assets/express/js/test/jest-e2e.config.js b/node-cli/assets/express/js/test/jest-e2e.config.js similarity index 100% rename from assets/express/js/test/jest-e2e.config.js rename to node-cli/assets/express/js/test/jest-e2e.config.js diff --git a/assets/express/ts/.gitignore b/node-cli/assets/express/ts/.gitignore similarity index 100% rename from assets/express/ts/.gitignore rename to node-cli/assets/express/ts/.gitignore diff --git a/assets/express/ts/jest.config.js b/node-cli/assets/express/ts/jest.config.js similarity index 100% rename from assets/express/ts/jest.config.js rename to node-cli/assets/express/ts/jest.config.js diff --git a/assets/express/ts/src/app.ts b/node-cli/assets/express/ts/src/app.ts similarity index 100% rename from assets/express/ts/src/app.ts rename to node-cli/assets/express/ts/src/app.ts diff --git a/assets/express/ts/src/hello-world.test.ts b/node-cli/assets/express/ts/src/hello-world.test.ts similarity index 100% rename from assets/express/ts/src/hello-world.test.ts rename to node-cli/assets/express/ts/src/hello-world.test.ts diff --git a/assets/express/ts/src/hello-world.ts b/node-cli/assets/express/ts/src/hello-world.ts similarity index 100% rename from assets/express/ts/src/hello-world.ts rename to node-cli/assets/express/ts/src/hello-world.ts diff --git a/assets/express/ts/src/index.ts b/node-cli/assets/express/ts/src/index.ts similarity index 100% rename from assets/express/ts/src/index.ts rename to node-cli/assets/express/ts/src/index.ts diff --git a/assets/express/ts/test/hello-world.spec-e2e.ts b/node-cli/assets/express/ts/test/hello-world.spec-e2e.ts similarity index 100% rename from assets/express/ts/test/hello-world.spec-e2e.ts rename to node-cli/assets/express/ts/test/hello-world.spec-e2e.ts diff --git a/assets/express/ts/test/jest-e2e.config.js b/node-cli/assets/express/ts/test/jest-e2e.config.js similarity index 100% rename from assets/express/ts/test/jest-e2e.config.js rename to node-cli/assets/express/ts/test/jest-e2e.config.js diff --git a/node-cli/assets/git/gitignorefile b/node-cli/assets/git/gitignorefile new file mode 100644 index 00000000..008b70cd --- /dev/null +++ b/node-cli/assets/git/gitignorefile @@ -0,0 +1,156 @@ +# This is a slightly modified version of https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# OS +.DS_Store + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage/ +coverage-e2e/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional prettier cache +.prettiercache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +# .env +.env* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Docker compose +docker-compose.override.yml + +# 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 diff --git a/node-cli/assets/licenses/licenses-allowed.js b/node-cli/assets/licenses/licenses-allowed.js new file mode 100644 index 00000000..2bee46b1 --- /dev/null +++ b/node-cli/assets/licenses/licenses-allowed.js @@ -0,0 +1,11 @@ +const spdxLicenses = require('spdx-license-list'); + +const separator = process.argv[2]; + +const osiApprovedLicenses = Object.entries(spdxLicenses) + .filter(([, value]) => value.osiApproved) + .map(([k]) => k); + +const reviewedLicenses = require('./licenses-reviewed'); + +console.log(osiApprovedLicenses.concat(reviewedLicenses).join(separator)); diff --git a/node-cli/assets/licenses/licenses-reviewed.js b/node-cli/assets/licenses/licenses-reviewed.js new file mode 100644 index 00000000..e7844022 --- /dev/null +++ b/node-cli/assets/licenses/licenses-reviewed.js @@ -0,0 +1,34 @@ +module.exports = [ + /** + * @link https://creativecommons.org/licenses/by/1.0/ + * + * Dependencies: + * spdx-license-ids + * spdx-license-list + */ + 'CC0-1.0', + + /** + * @link https://creativecommons.org/licenses/by/3.0/ + * + * Dependencies: + * spdx-ranges + */ + 'CC-BY-3.0', + + /** + * @link https://creativecommons.org/licenses/by/4.0/ + * + * Dependencies: + * caniuse-lite + */ + 'CC-BY-4.0', + + /** + * @link https://blueoakcouncil.org/license/1.0.0 + * + * Dependencies: + * markdownlint-cli + */ + 'BlueOak-1.0.0', +]; diff --git a/assets/local-scripts/initiate-detect-secrets.sh b/node-cli/assets/local-scripts/initiate-detect-secrets.sh similarity index 100% rename from assets/local-scripts/initiate-detect-secrets.sh rename to node-cli/assets/local-scripts/initiate-detect-secrets.sh diff --git a/node-cli/assets/nest/example-app-mongodb/.eslintrc.js b/node-cli/assets/nest/example-app-mongodb/.eslintrc.js new file mode 100644 index 00000000..259de13c --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + 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/assets/nest/example-app-mongodb/.gitignore b/node-cli/assets/nest/example-app-mongodb/.gitignore similarity index 100% rename from assets/nest/example-app-mongodb/.gitignore rename to node-cli/assets/nest/example-app-mongodb/.gitignore diff --git a/node-cli/assets/nest/example-app-mongodb/.ls-lint.yml b/node-cli/assets/nest/example-app-mongodb/.ls-lint.yml new file mode 100644 index 00000000..024d598f --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/.ls-lint.yml @@ -0,0 +1,30 @@ +ls: + # directories + .dir: kebab-case | regex:__tests__ | regex:__mocks__ | regex:__data__ + + # shell scripts + .sh: kebab-case + + # source files + # this is essentially kebab-case.with-custom.extentions.js + .js: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + .ts: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + + # migrations + migrations: + .ts: snake_case + +# ignored directories and files +ignore: + - node_modules + - .git + - .github + - .vscode + - .husky + - dist + - .commitlintrc.js + - .eslintrc.js + - .prettierrc.js + - .openapi + - src/@types + - .devcontainer diff --git a/node-cli/assets/nest/example-app-mongodb/.openapi/gitignorefile b/node-cli/assets/nest/example-app-mongodb/.openapi/gitignorefile new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/.openapi/gitignorefile @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/node-cli/assets/nest/example-app-mongodb/docker-compose.override.example.yml b/node-cli/assets/nest/example-app-mongodb/docker-compose.override.example.yml new file mode 100644 index 00000000..48d10555 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/docker-compose.override.example.yml @@ -0,0 +1,6 @@ +services: + + # uncomment the lines below and adjust the volume if desired + # db: + # volumes: + # - /path/to/volume:/var/lib/mongodb/data diff --git a/node-cli/assets/nest/example-app-mongodb/docker-compose.yml b/node-cli/assets/nest/example-app-mongodb/docker-compose.yml new file mode 100644 index 00000000..4e8ba19a --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/docker-compose.yml @@ -0,0 +1,45 @@ +version: '3' + +services: + db: + image: mongo:latest + container_name: my-mongodb + ports: + - '27017:27017' + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD} + command: --auth + volumes: + - db-data:/var/lib/mongodb/data + + api: + build: + context: . + dockerfile: Dockerfile + target: dev + container_name: todo-api + image: todo-api-dev + ports: + - '${PORT}:${PORT}' + env_file: + - .env + # Mount host directory to docker container to support watch mode + volumes: + - .:/usr/src/app + # This ensures that the NestJS container manages the node_modules folder + # rather than synchronizes it with the host machine + - api-node_modules:/usr/src/app/node_modules + depends_on: + - db + command: > + sh -c "cd /usr/src/app + && npm run start:dev" + +volumes: + api-node_modules: + db-data: + +networks: + default: + name: nest-api diff --git a/node-cli/assets/nest/example-app-mongodb/jest.config.js b/node-cli/assets/nest/example-app-mongodb/jest.config.js new file mode 100644 index 00000000..9bdd7b7a --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/jest.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + collectCoverageFrom: ['/src/**/*.(t|j)s'], + coverageDirectory: './coverage', + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/src/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*controller.ts$', + '/src/utils/class-transformers/.*', + '/src/database/seeds/.*', + '/src/.*interceptor.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '.', + testEnvironment: 'node', + testRegex: '.*\\.spec\\.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + '^@test/(.*)$': '/test/$1', + }, +}; diff --git a/node-cli/assets/nest/example-app-mongodb/jest.setup.ts b/node-cli/assets/nest/example-app-mongodb/jest.setup.ts new file mode 100644 index 00000000..fe68937d --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/jest.setup.ts @@ -0,0 +1,4 @@ +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/node-cli/assets/nest/example-app-mongodb/migrations/latest.ts b/node-cli/assets/nest/example-app-mongodb/migrations/latest.ts new file mode 100644 index 00000000..c61f9f41 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/migrations/latest.ts @@ -0,0 +1,8 @@ +import { Collections } from '@database/constants'; +import { Db } from 'mongodb'; + +export async function latest(mongodb: Db) { + await mongodb.createCollection(Collections.Users); + await mongodb.createCollection(Collections.Todos); + await mongodb.createIndex(Collections.Todos, 'user_id'); +} diff --git a/node-cli/assets/nest/example-app-mongodb/migrations/rollback.ts b/node-cli/assets/nest/example-app-mongodb/migrations/rollback.ts new file mode 100644 index 00000000..f6ef1318 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/migrations/rollback.ts @@ -0,0 +1,7 @@ +import { Collections } from '@database/constants'; +import { Db } from 'mongodb'; + +export async function rollback(mongodb: Db) { + await mongodb.dropCollection(Collections.Todos); + await mongodb.dropCollection(Collections.Users); +} diff --git a/node-cli/assets/nest/example-app-mongodb/nest-cli.json b/node-cli/assets/nest/example-app-mongodb/nest-cli.json new file mode 100644 index 00000000..f9aa683b --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.controller.ts b/node-cli/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.controller.ts new file mode 100644 index 00000000..d898fd58 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.controller.ts @@ -0,0 +1,50 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '@utils/decorators'; + +@ApiTags('Healthchecks') +@Controller('healthz') +export class HealthchecksController { + @Public() + @Get('live') + @ApiOperation({ + summary: 'Liveness endpoint', + description: 'Used to check whether the application is alive.', + }) + @ApiOkResponse({ + description: 'Application is alive', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + live(): string { + return 'OK'; + } + + @Public() + @Get('ready') + @ApiOperation({ + summary: 'Readiness endpoint', + description: + 'Used to check whether the application is ready to receive requests.', + }) + @ApiOkResponse({ + description: 'Application is ready', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + ready(): string { + return 'OK'; + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.module.ts b/node-cli/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.module.ts new file mode 100644 index 00000000..797561fc --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/healthchecks/healthchecks.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HealthchecksController } from './healthchecks.controller'; + +@Module({ + controllers: [HealthchecksController], +}) +export class HealthchecksModule {} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/__mocks__/index.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/__mocks__/index.ts new file mode 100644 index 00000000..3f42ef2e --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/__mocks__/index.ts @@ -0,0 +1 @@ +export * from './todos.mocks'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/__mocks__/todos.mocks.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/__mocks__/todos.mocks.ts new file mode 100644 index 00000000..ccd08437 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/__mocks__/todos.mocks.ts @@ -0,0 +1,85 @@ +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { Paginated, SortOrder } from '@utils/query'; +import { Todo } from '../entities'; +import { TodosSortBy, UpdateTodoDto } from '../dto'; +import { UserData } from '@api/auth/interfaces'; +import { ObjectId } from 'mongodb'; + +export const mockedUser: UserData = { + user: { + sub: new ObjectId(100).toString(), + email: 'example@email.com', + }, +}; + +const userId = new ObjectId(mockedUser.user.sub); + +export const todo: Todo = { + _id: new ObjectId(1), + name: 'todo', + note: null, + completed: false, + createdAt: new Date('2023-09-01T09:44:15.515Z').getTime(), + updatedAt: new Date('2023-09-01T09:44:15.515Z').getTime(), + userId, +}; + +export const secondTodo: Todo = { + _id: new ObjectId(2), + name: 'second-todo', + note: null, + completed: false, + createdAt: new Date('2023-09-02:44:15.515Z').getTime(), + updatedAt: new Date('2023-09-02:44:15.515Z').getTime(), + userId, +}; + +export const createTodoInput: CreateTodoInput = { + createTodoDto: { + name: todo.name, + completed: todo.completed, + note: todo.note, + }, + userId, +}; + +export const updateTodoDtoInput: UpdateTodoDto = { + name: 'new name', + note: 'new note', + completed: true, +}; + +export const updateTodoInput: UpdateTodoInput = { + _id: todo._id, + updateTodoDto: updateTodoDtoInput, + userId, +}; + +export const findOneTodoInput: FindOneTodoInput = { + _id: todo._id, + userId, +}; + +export const findAllTodosInput: FindAllTodosInput = { + query: { + pageNumber: 1, + pageSize: 20, + column: TodosSortBy.CreatedAt, + order: SortOrder.Asc, + }, + userId, +}; + +export const getPaginatedResponse = (data: T[]): Paginated => { + return { + items: data, + total: data.length, + totalPages: 1, + currentPage: 1, + }; +}; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/create-todo.dto.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/create-todo.dto.ts new file mode 100644 index 00000000..c100f414 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/create-todo.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Trim } from '@utils/class-transformers'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; + +export class CreateTodoDto { + @ApiProperty() + @Trim() + @MinLength(1) + @MaxLength(255) + name: string; + + @ApiProperty({ type: String, required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/find-all-todos-query.dto.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/find-all-todos-query.dto.ts new file mode 100644 index 00000000..2fd6a7b8 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/find-all-todos-query.dto.ts @@ -0,0 +1,75 @@ +import { SortOrder } from '@utils/query'; +import { + Allow, + IsEnum, + IsNumber, + IsOptional, + MaxLength, + MinLength, +} from 'class-validator'; +import { Transform } from 'class-transformer'; +import { Trim } from '@utils/class-transformers'; +import { ApiProperty } from '@nestjs/swagger'; + +export enum TodosSortBy { + Name = 'name', + CreatedAt = 'createdAt', +} + +export class FindAllTodosQueryDto { + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ + type: Boolean, + required: false, + }) + @Transform(({ value }) => + value === 'true' ? true : value === 'false' ? false : undefined + ) + @IsOptional() + completed?: boolean; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @IsEnum(TodosSortBy) + column?: TodosSortBy; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @IsEnum(SortOrder) + @Allow(undefined) + order?: SortOrder | undefined; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number = 1; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number = 20; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/index.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/index.ts new file mode 100644 index 00000000..ea59f55f --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-todo.dto'; +export * from './find-all-todos-query.dto'; +export * from './update-todo.dto'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/update-todo.dto.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/update-todo.dto.ts new file mode 100644 index 00000000..e00f0533 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/dto/update-todo.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateTodoDto } from './create-todo.dto'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; +import { Trim } from '@utils/class-transformers'; + +export class UpdateTodoDto extends PartialType(CreateTodoDto) { + @ApiProperty({ required: false }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/entities/index.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/entities/index.ts new file mode 100644 index 00000000..69b0759f --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/entities/index.ts @@ -0,0 +1 @@ +export * from './todo.entity'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/entities/todo.entity.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/entities/todo.entity.ts new file mode 100644 index 00000000..69be3141 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/entities/todo.entity.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ToObjectId } from '@utils/class-transformers'; +import { GenericEntity } from '@utils/entities/generic-entity'; +import { ObjectId } from 'mongodb'; + +export class Todo extends GenericEntity { + @ApiProperty() + @ToObjectId() + userId: ObjectId; + + @ApiProperty() + name: string; + + @ApiProperty({ nullable: true }) + note: string | null; + + @ApiProperty({ default: false }) + completed = false; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/error-mappings/index.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/error-mappings/index.ts new file mode 100644 index 00000000..cec33482 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './todo-user-not-found.error-mapping'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts new file mode 100644 index 00000000..de7ad0d7 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts @@ -0,0 +1,3 @@ +import { recordNotFoundErrorMapping } from '@database/error-mappings'; + +export const TodoUserNotFound = recordNotFoundErrorMapping('User not found'); diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/interfaces/index.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/interfaces/index.ts new file mode 100644 index 00000000..1faae003 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/interfaces/index.ts @@ -0,0 +1 @@ +export * from './todos.interface'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/interfaces/todos.interface.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/interfaces/todos.interface.ts new file mode 100644 index 00000000..312948fe --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/interfaces/todos.interface.ts @@ -0,0 +1,24 @@ +import { ObjectId } from 'mongodb'; +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from '../dto'; + +interface UserData { + userId: ObjectId; +} + +interface TodoId { + _id?: ObjectId; +} + +export interface CreateTodoInput extends UserData { + createTodoDto: CreateTodoDto; +} + +export interface FindAllTodosInput extends UserData { + query: FindAllTodosQueryDto; +} + +export interface FindOneTodoInput extends UserData, TodoId, UpdateTodoDto {} + +export interface UpdateTodoInput extends FindOneTodoInput { + updateTodoDto: UpdateTodoDto; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/index.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/index.ts new file mode 100644 index 00000000..2639d438 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/index.ts @@ -0,0 +1 @@ +export * from './todos.repository'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.spec.ts new file mode 100644 index 00000000..93121166 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.spec.ts @@ -0,0 +1,169 @@ +import { Test } from '@nestjs/testing'; +import { TodosRepository } from './todos.repository'; +import { + createTodoInput, + findAllTodosInput, + findOneTodoInput, + mockedUser, + secondTodo, + todo, + updateTodoInput, +} from '../__mocks__'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; +import { SortOrder } from '@utils/query'; + +describe('TodosRepository', () => { + let todosRepository: TodosRepository; + + const mockFn = jest.fn().mockImplementation(() => Promise.resolve([])); + + const toArray = jest.fn().mockImplementation(() => Promise.resolve([])); + const sort = jest.fn().mockImplementation(() => ({ + skip: () => ({ limit: () => ({ toArray }) }), + })); + const find = jest.fn().mockImplementation(() => ({ + sort, + })); + + const insertOne = jest.fn().mockImplementation(async () => mockFn()); + const findOne = jest.fn().mockImplementation(() => mockFn()); + const findOneAndUpdate = jest.fn().mockImplementation(() => mockFn()); + const findOneAndDelete = jest.fn().mockImplementation(() => mockFn()); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: DatabaseService, + useFactory: () => ({ + connection: { + collection: () => ({ + insertOne, + find, + findOne, + findOneAndUpdate, + findOneAndDelete, + }), + }, + }), + }, + TodosRepository, + ], + }).compile(); + + todosRepository = moduleRef.get(TodosRepository); + }); + + it('create - create a todo', async () => { + mockFn.mockImplementationOnce(() => + Promise.resolve({ insertedId: todo._id }) + ); + + const result = await todosRepository.create(createTodoInput); + + expect(result).toBe(todo._id); + expect(insertOne).toHaveBeenCalledWith( + expect.objectContaining({ + ...createTodoInput.createTodoDto, + userId: createTodoInput.userId, + }) + ); + }); + + it('findOne - find a todo', async () => { + mockFn.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOne(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(findOne).toHaveBeenCalledWith(findOneTodoInput); + }); + + it('findOneOrFail - find a todo', async () => { + mockFn.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOneOrFail(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(findOne).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('update', () => { + it('update - update a todo', async () => { + const updatedTodo = { + ...todo, + name: updateTodoInput.updateTodoDto.name, + }; + + mockFn.mockImplementationOnce(() => Promise.resolve(updatedTodo)); + + const result = await todosRepository.update(updateTodoInput); + + expect(result).toStrictEqual(updatedTodo); + expect(findOneAndUpdate).toHaveBeenCalledWith( + { + _id: updateTodoInput._id, + userId: new ObjectId(mockedUser.user.sub), + }, + { + $set: { + completed: updateTodoInput.updateTodoDto.completed, + name: updateTodoInput.updateTodoDto.name, + note: updateTodoInput.updateTodoDto.note, + }, + }, + { returnDocument: 'after' } + ); + }); + }); + + it('remove - delete a todo', async () => { + mockFn.mockImplementationOnce(() => Promise.resolve(todo)); + + const result = await todosRepository.remove(findOneTodoInput); + + expect(result).toBe(todo); + expect(findOneAndDelete).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('findAll', () => { + it('find all todos for the user sorted by `createdAt` in ascending order', async () => { + toArray.mockImplementationOnce(() => Promise.resolve([todo, secondTodo])); + jest.spyOn(todosRepository, 'count').mockResolvedValueOnce(2); + + const result = await todosRepository.findAll(findAllTodosInput); + + expect(sort).toHaveBeenCalledWith('createdAt', 1); + expect(result).toStrictEqual({ + items: [todo, secondTodo], + total: 2, + currentPage: 1, + totalPages: 1, + }); + }); + + it('find all todos for the user sorted by `createdAt` in descending order', async () => { + toArray.mockImplementationOnce(() => Promise.resolve([secondTodo, todo])); + jest.spyOn(todosRepository, 'count').mockResolvedValueOnce(2); + const { userId, query } = findAllTodosInput; + + const result = await todosRepository.findAll({ + userId, + query: { + ...query, + order: SortOrder.Desc, + }, + }); + + expect(sort).toHaveBeenCalledWith('createdAt', -1); + expect(result).toStrictEqual({ + items: [secondTodo, todo], + total: 2, + currentPage: 1, + totalPages: 1, + }); + }); + }); +}); + diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.ts new file mode 100644 index 00000000..e5835960 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/repositories/todos.repository.ts @@ -0,0 +1,110 @@ +import { rethrowError } from '@utils/error'; +import { Paginated } from '@utils/query/pagination'; +import { Todo } from '../entities'; +import { Injectable } from '@nestjs/common'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { definedOrNotFound, SortOrder } from '@utils/query'; +import { TodoUserNotFound } from '../error-mappings'; +import { paginatedResponse } from '@utils/api/response'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; +import { Errors } from '@utils/enums'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; +import { NullableKeysPartial } from '@utils/interfaces'; + +@Injectable() +export class TodosRepository extends BaseRepository { + constructor(private readonly mongodb: DatabaseService) { + super(mongodb, Tables.Todos); + } + + async create(input: CreateTodoInput): Promise { + const todo = { + _id: new ObjectId(), + userId: input.userId, + ...input.createTodoDto, + // Why do we need it in the input? + completed: false, + createdAt: Date.now(), + updatedAt: Date.now(), + }; + + return this.repository() + .insertOne(todo) + .then((value) => value.insertedId) + .catch(rethrowError(TodoUserNotFound)); + } + + async findOne( + input: Partial, + ): Promise | null> { + return this.repository().findOne(input); + } + + async findOneOrFail( + input: Partial, + ): Promise> { + return this.repository() + .findOne(input) + .then(definedOrNotFound(Errors.NotFound)); + } + + async update( + input: UpdateTodoInput, + ): Promise | null> { + const { _id, userId, updateTodoDto } = input; + + return this.repository() + .findOneAndUpdate( + { _id, userId }, + { $set: updateTodoDto }, + { returnDocument: 'after' }, + ) + .catch(rethrowError(TodoUserNotFound)); + } + + async remove( + input: FindOneTodoInput, + ): Promise | null> { + return this.repository().findOneAndDelete(input); + } + + async findAll( + input: FindAllTodosInput, + ): Promise>> { + const { + userId, + query: { pageNumber, pageSize, name, completed, column, order }, + } = input; + + const pagination = { pageNumber, pageSize }; + const filters = Object.entries({ userId, name, completed }) + .filter(([, v]) => v !== undefined) + .reduce((obj: Record, [k, v]) => { + obj[k] = v; + return obj; + }, {}); + + let cursor = this.repository().find(filters); + + if (column) { + const sortOrder = order === SortOrder.Asc ? 1 : -1; + cursor = cursor.sort(column, sortOrder); + } + + const items = await cursor + .skip(((pageNumber as number) - 1) * (pageSize as number)) + .limit(pageSize as number) + .toArray(); + + const count = await this.count(); + + return paginatedResponse(items, count, pagination); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.controller.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.controller.ts new file mode 100644 index 00000000..1c5541e2 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.controller.ts @@ -0,0 +1,116 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Delete, + Req, + Put, + Query, +} from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { + ApiBadRequestResponse, + ApiBody, + ApiCreatedResponse, + ApiNotFoundResponse, + ApiOkResponse, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { Todo } from './entities'; +import { Paginated } from '@utils/query'; +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from './dto'; +import { UserData } from '@api/auth/interfaces'; +import { + BadRequestDto, + NotFoundDto, + UnprocessableEntityDto, +} from '@utils/dtos'; +import { Errors } from '@utils/enums'; +import { ObjectId } from 'mongodb'; +import { NullableKeysPartial } from '@utils/interfaces'; +import { GenericEntity } from '@utils/entities'; +import { ToObjectIdPipe } from '@utils/pipes'; + +@ApiTags('Todos') +@Controller('v1/todos') +export class TodosController { + constructor(private readonly todosService: TodosService) {} + + @ApiBody({ type: CreateTodoDto }) + @ApiCreatedResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @ApiUnprocessableEntityResponse({ + type: UnprocessableEntityDto, + description: Errors.UnprocessableEntity, + }) + @Post() + create( + @Body() createTodoDto: CreateTodoDto, + @Req() { user: { sub } }: UserData + ): Promise { + return this.todosService.create({ + createTodoDto, + userId: new ObjectId(sub), + }); + } + + @ApiOkResponse({ type: Todo, isArray: true }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @Get() + findAll( + @Req() { user: { sub } }: UserData, + @Query() query: FindAllTodosQueryDto + ): Promise>> { + return this.todosService.findAll({ userId: new ObjectId(sub), query }); + } + + @ApiOkResponse({ type: Todo }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Get(':id') + findOne( + @Param('id', ToObjectIdPipe) _id: ObjectId, + @Req() { user: { sub } }: UserData + ): Promise> { + return this.todosService.findOneOrFail({ _id, userId: new ObjectId(sub) }); + } + + @ApiBody({ type: UpdateTodoDto }) + @ApiOkResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Put(':id') + async update( + @Param('id', ToObjectIdPipe) _id: ObjectId, + @Req() { user: { sub } }: UserData, + @Body() updateTodoDto: UpdateTodoDto + ): Promise> { + return await this.todosService.update({ + _id, + userId: new ObjectId(sub), + updateTodoDto, + }); + } + + @ApiOkResponse({ type: Number }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Delete(':id') + remove( + @Param('id', ToObjectIdPipe) _id: GenericEntity['_id'], + @Req() { user: { sub } }: UserData + ): Promise { + return this.todosService.remove({ _id, userId: new ObjectId(sub) }); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.module.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.module.ts new file mode 100644 index 00000000..20fefaa8 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { DatabaseModule } from '@database/database.module'; +import { TodosRepository } from './repositories/todos.repository'; + +@Module({ + imports: [DatabaseModule], + controllers: [TodosController], + providers: [TodosService, TodosRepository], +}) +export class TodosModule {} diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.service.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.service.spec.ts new file mode 100644 index 00000000..a2eee84a --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.service.spec.ts @@ -0,0 +1,183 @@ +import { DatabaseModule } from '@database/database.module'; +import { Test, TestingModule } from '@nestjs/testing'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { TodosRepository } from './repositories'; +import { + createTodoInput, + mockedUser, + getPaginatedResponse, + todo, + updateTodoDtoInput, +} from './__mocks__'; +import { + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { Errors } from '@utils/enums'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; +import { ConfigModule } from '@nestjs/config'; +import { ObjectId } from 'mongodb'; +import { DatabaseService } from '@database/database.service'; + +describe('TodosService', () => { + let service: TodosService; + let repository: TodosRepository; + const userId = mockedUser.user.sub; + + class DatabaseServiceMock { + public connection = {}; + public client = {}; + } + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + DatabaseModule, + ], + controllers: [TodosController], + providers: [TodosService, TodosRepository], + }) + .overrideProvider(DatabaseService) + .useClass(DatabaseServiceMock) + .compile(); + + service = module.get(TodosService); + repository = module.get(TodosRepository); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('create', () => { + it('should return created todo', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => null); + jest + .spyOn(repository, 'create') + .mockImplementationOnce(async () => todo._id); + + expect(await service.create(createTodoInput)).toStrictEqual(todo._id); + }); + + it('should throw error if todo with the same name exists', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => todo); + + await expect(service.create(createTodoInput)).rejects.toThrow( + new UnprocessableEntityException(Errors.UnprocessableEntity) + ); + }); + }); + + describe('findAll', () => { + it('should return todos', async () => { + const paginatedResponse = getPaginatedResponse([todo]); + + jest + .spyOn(repository, 'findAll') + .mockImplementationOnce(async () => paginatedResponse); + + expect( + await service.findAll({ userId: new ObjectId(userId), query: {} }) + ).toStrictEqual(paginatedResponse); + }); + }); + + describe('findOne', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.findOneOrFail({ + _id: todo._id, + userId: new ObjectId(userId), + }) + ).toStrictEqual(todo); + }); + }); + + describe('findOneOrFail', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.findOneOrFail({ + _id: todo._id, + userId: new ObjectId(userId), + }) + ).toStrictEqual(todo); + }); + }); + + describe('update', () => { + it('should update single todo', async () => { + const updatedTodo = { ...todo, ...updateTodoDtoInput }; + + jest + .spyOn(repository, 'update') + .mockImplementationOnce(async () => updatedTodo); + + expect( + await service.update({ + _id: todo._id, + userId: new ObjectId(userId), + updateTodoDto: updateTodoDtoInput, + }) + ).toStrictEqual(updatedTodo); + }); + + it('should return todo if there is no data to update', async () => { + jest + .spyOn(service, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.update({ + _id: todo._id, + userId: new ObjectId(userId), + updateTodoDto: {}, + }) + ).toStrictEqual(todo); + }); + }); + + describe('remove', () => { + it('should delete single todo', async () => { + jest.spyOn(repository, 'remove').mockImplementationOnce(async () => todo); + + expect( + await service.remove({ + _id: new ObjectId(), + userId: new ObjectId(userId), + }) + ).toBe(true); + }); + + it('should throw error if todo does not exist', async () => { + jest.spyOn(repository, 'remove').mockImplementationOnce(async () => null); + + await expect( + service.remove({ _id: new ObjectId(), userId: new ObjectId(userId) }) + ).rejects.toThrow(new NotFoundException(Errors.NotFound)); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.service.ts b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.service.ts new file mode 100644 index 00000000..9fb1c00c --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/api/todos/todos.service.ts @@ -0,0 +1,74 @@ +import { + Inject, + Injectable, + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { TodosRepository } from './repositories'; +import { Paginated, definedOrNotFound } from '@utils/query'; +import { Todo } from './entities/todo.entity'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from './interfaces'; +import { Errors } from '@utils/enums'; +import { NullableKeysPartial } from '@utils/interfaces'; +import { ObjectId } from 'mongodb'; + +@Injectable() +export class TodosService { + constructor( + @Inject(TodosRepository) + private readonly todos: TodosRepository + ) {} + + async create(input: CreateTodoInput): Promise { + const { + userId, + createTodoDto: { name }, + } = input; + + const todo = await this.todos.findOne({ userId, name }); + + if (todo) { + throw new UnprocessableEntityException(Errors.UnprocessableEntity); + } + + return this.todos.create(input); + } + + findAll( + input: FindAllTodosInput + ): Promise>> { + return this.todos.findAll(input); + } + + findOneOrFail( + input: Partial + ): Promise> { + return this.todos.findOneOrFail(input); + } + + update(input: UpdateTodoInput): Promise> { + const { _id, userId, updateTodoDto } = input; + + if (Object.keys(updateTodoDto).length === 0) { + return this.findOneOrFail({ _id, userId }); + } + + return this.todos.update(input).then(definedOrNotFound(Errors.NotFound)); + } + + async remove(input: FindOneTodoInput): Promise { + input._id = new ObjectId(input._id); + + const deletedTodo = await this.todos.remove(input); + if (!deletedTodo) { + throw new NotFoundException(Errors.NotFound); + } + + return true; + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/app.module.ts b/node-cli/assets/nest/example-app-mongodb/src/app.module.ts new file mode 100644 index 00000000..6f383b64 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/app.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DatabaseModule } from '@database/database.module'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; +import { AuthModule } from '@api/auth/auth.module'; +import { UsersModule } from '@api/users/users.module'; +import { TodosModule } from '@api/todos/todos.module'; +import { nodeConfig, dbConfig, authConfig } from '@utils/environment'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + cache: true, + ignoreEnvFile: true, + isGlobal: true, + }), + DatabaseModule, + HealthchecksModule, + UsersModule, + AuthModule, + TodosModule, + ], +}) +export class AppModule {} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/base-repository.repository.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/database/base-repository.repository.spec.ts new file mode 100644 index 00000000..d3afa7a9 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/base-repository.repository.spec.ts @@ -0,0 +1,40 @@ +import { Test } from '@nestjs/testing'; +import { BaseRepository } from './base-repository.repository'; +import { DatabaseService } from './database.service'; + +describe('BaseRepository', () => { + let baseRepository: BaseRepository; + + const estimatedDocumentCount = jest.fn( + (): Promise => Promise.resolve() + ); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: DatabaseService, + useFactory: () => ({ + connection: { + collection: () => ({ + estimatedDocumentCount, + }), + }, + }), + }, + BaseRepository, + ], + }).compile(); + + const databaseService = moduleRef.get(DatabaseService); + baseRepository = new BaseRepository(databaseService, 'test'); + }); + + it('count', async () => { + estimatedDocumentCount.mockResolvedValueOnce(1); + + const result = await baseRepository.count(); + + expect(result).toBe(1); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/base-repository.repository.ts b/node-cli/assets/nest/example-app-mongodb/src/database/base-repository.repository.ts new file mode 100644 index 00000000..3b4db097 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/base-repository.repository.ts @@ -0,0 +1,23 @@ +import { NullableKeysPartial } from '@utils/interfaces'; +import { DatabaseService } from './database.service'; + +export class BaseRepository> { + private dbService; + + constructor( + mongodb: DatabaseService, + private readonly collectionName: string, + ) { + this.dbService = mongodb; + } + + repository() { + return this.dbService.connection.collection>( + this.collectionName, + ); + } + + async count(): Promise { + return await this.repository().estimatedDocumentCount(); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/constants.ts b/node-cli/assets/nest/example-app-mongodb/src/database/constants.ts new file mode 100644 index 00000000..de798394 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/constants.ts @@ -0,0 +1,12 @@ +export const MONGO_CONNECTION = 'MONGO_CONNECTION'; +export const NEST_MONGO_OPTIONS = 'NEST_MONGO_OPTIONS'; + +export enum Tables { + Users = 'users', + Todos = 'todos', +} + +export enum Collections { + Users = 'users', + Todos = 'todos', +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/database.module.ts b/node-cli/assets/nest/example-app-mongodb/src/database/database.module.ts new file mode 100644 index 00000000..b32d2400 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/database.module.ts @@ -0,0 +1,43 @@ +import { Module, OnApplicationShutdown } from '@nestjs/common'; +import { ConfigModule, ConfigType } from '@nestjs/config'; + +import { NEST_MONGO_OPTIONS } from './constants'; +import { DatabaseService } from './database.service'; +import { dbConfig } from '@utils/environment'; + +@Module({ + imports: [ConfigModule], + providers: [ + DatabaseService, + { + provide: NEST_MONGO_OPTIONS, + inject: [dbConfig.KEY], + useFactory: (database: ConfigType) => { + const protocol = database.MONGO_PROTOCOL; + const host = database.MONGO_HOST; + const port = database.MONGO_PORT; + const user = database.MONGO_USER; + const password = database.MONGO_PASSWORD; + const databaseName = database.MONGO_DATABASE_NAME; + const delimiter = user && password ? ':' : ''; + const at = delimiter ? '@' : ''; + + const urlString = `${protocol}://${user}${delimiter}${password}${at}${host}:${port}`; + + return { + urlString, + databaseName, + clientOptions: {}, + }; + }, + }, + ], + exports: [DatabaseService], +}) +export class DatabaseModule implements OnApplicationShutdown { + constructor(private readonly mongodb: DatabaseService) {} + + async onApplicationShutdown(): Promise { + await this.mongodb.client.close(); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/database.service.ts b/node-cli/assets/nest/example-app-mongodb/src/database/database.service.ts new file mode 100644 index 00000000..1a56a52d --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/database.service.ts @@ -0,0 +1,27 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { NEST_MONGO_OPTIONS } from './constants'; +import { MongoClient, MongoClientOptions, Db } from 'mongodb'; + +@Injectable() +export class DatabaseService { + public connection: Db; + public client: MongoClient; + + constructor( + @Inject(NEST_MONGO_OPTIONS) + nestMongoOptions: { + urlString: string; + databaseName: string; + clientOptions: MongoClientOptions; + }, + ) { + const client = new MongoClient( + nestMongoOptions.urlString, + nestMongoOptions.clientOptions, + ); + const { databaseName } = nestMongoOptions; + + this.connection = client.db(databaseName); + this.client = client; + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/index.ts b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/index.ts new file mode 100644 index 00000000..297377f0 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/index.ts @@ -0,0 +1,2 @@ +export * from './record-not-found.error-mapping'; +export * from './unique-violation.error-mapping'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.spec.ts new file mode 100644 index 00000000..bbf3b076 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.spec.ts @@ -0,0 +1,38 @@ +import { RecordNotFoundError } from '@database/errors'; +import { MongoError } from 'mongodb'; +import { recordNotFoundErrorMapping } from './record-not-found.error-mapping'; + +describe('recordNotFound', () => { + it('should return an error mapping', () => { + expect(recordNotFoundErrorMapping('error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = recordNotFoundErrorMapping('error-message').isError; + + it('should return true when the error is a MongoDB error', () => { + const error = new MongoError('error'); + error.code = 1; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a MongoDB error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = recordNotFoundErrorMapping('error-message').newError; + + it('should return an instance of RecordNotFound with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(RecordNotFoundError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.ts b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.ts new file mode 100644 index 00000000..f0233269 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/record-not-found.error-mapping.ts @@ -0,0 +1,8 @@ +import { RecordNotFoundError } from '@database/errors'; +import { isDatabaseError } from '@database/utils'; +import { ErrorMapping } from '@utils/error'; + +export const recordNotFoundErrorMapping = (message: string): ErrorMapping => ({ + isError: isDatabaseError, + newError: () => new RecordNotFoundError(message), +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.spec.ts new file mode 100644 index 00000000..563d910d --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.spec.ts @@ -0,0 +1,38 @@ +import { uniqueViolation } from './unique-violation.error-mapping'; +import { DuplicateRecordError } from '@database/errors'; +import { MongoError } from 'mongodb'; + +describe('uniqueViolation', () => { + it('should return an error mapping', () => { + expect(uniqueViolation('error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = uniqueViolation('error-message').isError; + + it('should return true when the error is a unique constraint violation error', () => { + const error = new MongoError('error'); + error.code = 11000; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a unique constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = uniqueViolation('error-message').newError; + + it('should return an instance of DuplicateRecord with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(DuplicateRecordError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.ts b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.ts new file mode 100644 index 00000000..b190ef2e --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/error-mappings/unique-violation.error-mapping.ts @@ -0,0 +1,8 @@ +import { DuplicateRecordError } from '@database/errors'; +import { isUniqueViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const uniqueViolation = (message: string): ErrorMapping => ({ + isError: isUniqueViolation, + newError: () => new DuplicateRecordError(message), +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/errors/duplicate-record.error.ts b/node-cli/assets/nest/example-app-mongodb/src/database/errors/duplicate-record.error.ts new file mode 100644 index 00000000..65fbab07 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/errors/duplicate-record.error.ts @@ -0,0 +1,6 @@ +export class DuplicateRecordError extends Error { + constructor(message: string) { + super(message); + this.name = DuplicateRecordError.name; + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/errors/index.ts b/node-cli/assets/nest/example-app-mongodb/src/database/errors/index.ts new file mode 100644 index 00000000..7099d226 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/errors/index.ts @@ -0,0 +1,2 @@ +export * from './duplicate-record.error'; +export * from './record-not-found.error'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/errors/record-not-found.error.ts b/node-cli/assets/nest/example-app-mongodb/src/database/errors/record-not-found.error.ts new file mode 100644 index 00000000..52bcb4fa --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/errors/record-not-found.error.ts @@ -0,0 +1,6 @@ +export class RecordNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = RecordNotFoundError.name; + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/utils/error.ts b/node-cli/assets/nest/example-app-mongodb/src/database/utils/error.ts new file mode 100644 index 00000000..80281f4e --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/utils/error.ts @@ -0,0 +1,7 @@ +import { MongoError } from 'mongodb'; + +export const isDatabaseError = (e: unknown): e is MongoError => + e instanceof MongoError && e.code !== undefined; + +export const isUniqueViolation = (e: unknown) => + isDatabaseError(e) && e.code === 11000; diff --git a/node-cli/assets/nest/example-app-mongodb/src/database/utils/index.ts b/node-cli/assets/nest/example-app-mongodb/src/database/utils/index.ts new file mode 100644 index 00000000..93ae819e --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/database/utils/index.ts @@ -0,0 +1 @@ +export * from './error'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/main.ts b/node-cli/assets/nest/example-app-mongodb/src/main.ts new file mode 100644 index 00000000..d27d0b48 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/main.ts @@ -0,0 +1,65 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import helmet from '@fastify/helmet'; +import compression from '@fastify/compress'; +import { HttpAdapterHost, NestFactory } from '@nestjs/core'; +import { ConfigType } from '@nestjs/config'; +import { AppModule } from './app.module'; +import { ValidationPipe } from '@nestjs/common'; +import { RequestLoggingInterceptor } from '@utils/interceptors/request-logging.interceptor'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { ErrorLoggingFilter } from '@utils/error-logging.filter'; +import { nodeConfig } from '@utils/environment'; + +async function bootstrap() { + const app = await NestFactory.create( + AppModule, + new FastifyAdapter(), + ); + + // enables CORS + app.enableCors(); + + // add security HTTP headers + app.register(helmet); + + // compresses response bodies + app.register(compression); + + // enable validation globally + app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); + + // map application level errors to http errors + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + // setup graceful shutdown + app.enableShutdownHooks(); + + const configService = app.get>(nodeConfig.KEY); + const port = configService.PORT; + + if (configService.REQUEST_LOGGING) { + app.useGlobalInterceptors(new RequestLoggingInterceptor()); + } + + if (configService.SWAGGER) { + const config = new DocumentBuilder().setTitle('To-Do Example API').build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('/swagger', app, document); + } + + if (configService.ERROR_LOGGING) { + const httpAdapterHost = app.get(HttpAdapterHost); + app.useGlobalFilters(new ErrorLoggingFilter(httpAdapterHost)); + } + + // start server + await app.listen(port, '0.0.0.0', () => { + console.log(`App is running on http://localhost:${port}`); + }); +} + +bootstrap(); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/api/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/api/index.ts new file mode 100644 index 00000000..dbc1ea0f --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/api/index.ts @@ -0,0 +1 @@ +export * from './response'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/api/response.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/api/response.ts new file mode 100644 index 00000000..0759d778 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/api/response.ts @@ -0,0 +1,16 @@ +import { Paginated, Pagination, extractPagination } from '@utils/query'; + +export const paginatedResponse = ( + items: T[], + total: number, + pagination?: Pagination +): Paginated => { + const { pageNumber, pageSize } = extractPagination(pagination); + + return { + items, + total: total, + currentPage: pageNumber, + totalPages: Math.ceil(total / pageSize), + }; +}; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/index.ts new file mode 100644 index 00000000..94fd1349 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/index.ts @@ -0,0 +1,3 @@ +export * from './lower-case'; +export * from './object-id'; +export * from './trim'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/lower-case.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/lower-case.ts new file mode 100644 index 00000000..dcc3b625 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/lower-case.ts @@ -0,0 +1,16 @@ +import { Transform } from 'class-transformer'; + +const toLower = (v: string) => v.toLowerCase(); + +export const LowerCase = () => + Transform( + (params) => + Array.isArray(params.value) + ? params.value.map(toLower) + : typeof params.value === 'string' + ? toLower(params.value) + : params.value, + { + toClassOnly: true, + } + ); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/object-id.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/object-id.ts new file mode 100644 index 00000000..0fa9e1ed --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/object-id.ts @@ -0,0 +1,16 @@ +import { BadRequestDto } from '@utils/dtos'; +import { Transform } from 'class-transformer'; +import { ObjectId } from 'mongodb'; + +const toObjectId = (v: string) => { + try { + return new ObjectId(v); + } catch (err) { + throw new BadRequestDto(); + } +}; + +export const ToObjectId = () => + Transform((params) => toObjectId(params.value), { + toClassOnly: true, + }); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/trim.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/trim.ts new file mode 100644 index 00000000..b261cbce --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/class-transformers/trim.ts @@ -0,0 +1,4 @@ +import { Transform } from 'class-transformer'; + +export const Trim = () => + Transform((params) => params.value?.trim(), { toClassOnly: true }); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/index.ts new file mode 100644 index 00000000..3f75d993 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/index.ts @@ -0,0 +1 @@ +export * from './public.decorator'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.spec.ts new file mode 100644 index 00000000..b4aab11b --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.spec.ts @@ -0,0 +1,20 @@ +import { IS_PUBLIC_KEY, Public } from './public.decorator'; + +describe('@Public', () => { + @Public() + class PublicRoutes {} + + class GuardedRoutes {} + + it('should be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, PublicRoutes); + + expect(isPublic).toBe(true); + }); + + it('should not be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, GuardedRoutes); + + expect(isPublic).toBe(undefined); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.ts new file mode 100644 index 00000000..b3845e12 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/decorators/public.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/errors.dto.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/errors.dto.ts new file mode 100644 index 00000000..661bfa22 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/errors.dto.ts @@ -0,0 +1,63 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Errors } from '@utils/enums'; + +interface ErrorDto { + statusCode: number; + message: string; + error: string; +} + +export class BadRequestDto implements ErrorDto { + @ApiProperty({ example: 400 }) + statusCode: number; + + @ApiProperty({ example: Errors.BadRequest }) + message: string; + + @ApiProperty({ example: Errors.BadRequest }) + error: string; +} + +export class UnauthorizedDto implements ErrorDto { + @ApiProperty({ example: 401 }) + statusCode: number; + + @ApiProperty({ example: Errors.Unauthorized }) + message: string; + + @ApiProperty({ example: Errors.Unauthorized }) + error: string; +} + +export class NotFoundDto implements ErrorDto { + @ApiProperty({ example: 404 }) + statusCode: number; + + @ApiProperty({ example: 'Record not found' }) + message: string; + + @ApiProperty({ example: Errors.NotFound }) + error: string; +} + +export class ConflictDto implements ErrorDto { + @ApiProperty({ example: 409 }) + statusCode: number; + + @ApiProperty({ example: 'Record already exists' }) + message: string; + + @ApiProperty({ example: Errors.Conflict }) + error: string; +} + +export class UnprocessableEntityDto implements ErrorDto { + @ApiProperty({ example: 422 }) + statusCode: number; + + @ApiProperty({ example: 'Invalid input' }) + message: string; + + @ApiProperty({ example: Errors.UnprocessableEntity }) + error: string; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/index.ts new file mode 100644 index 00000000..1a520955 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/index.ts @@ -0,0 +1,2 @@ +export * from './pagination.dto'; +export * from './errors.dto'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/pagination.dto.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/pagination.dto.ts new file mode 100644 index 00000000..0bdf43ae --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/dtos/pagination.dto.ts @@ -0,0 +1,14 @@ +import { Transform } from 'class-transformer'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class PaginationDto { + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/entities/generic-entity.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/entities/generic-entity.ts new file mode 100644 index 00000000..1d2e949c --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/entities/generic-entity.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ToObjectId } from '@utils/class-transformers'; +import { ObjectId } from 'mongodb'; + +export class GenericEntity { + @ApiProperty() + @ToObjectId() + _id: ObjectId; + + @ApiProperty() + createdAt: number; + + @ApiProperty() + updatedAt: number; +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/entities/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/entities/index.ts new file mode 100644 index 00000000..1fdd9774 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/entities/index.ts @@ -0,0 +1 @@ +export * from './generic-entity'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/enums/errors.enum.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/enums/errors.enum.ts new file mode 100644 index 00000000..8d775147 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/enums/errors.enum.ts @@ -0,0 +1,7 @@ +export enum Errors { + BadRequest = 'Bad Request', + Unauthorized = 'Unauthorized', + NotFound = 'Not Found', + Conflict = 'Conflict', + UnprocessableEntity = 'Unprocessable Entity', +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/enums/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/enums/index.ts new file mode 100644 index 00000000..efe62e58 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/enums/index.ts @@ -0,0 +1 @@ +export * from './errors.enum'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/error-logging.filter.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/error-logging.filter.spec.ts new file mode 100644 index 00000000..8b6e8362 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/error-logging.filter.spec.ts @@ -0,0 +1,68 @@ +import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core'; +import { + ArgumentsHost, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { ErrorLoggingFilter } from './error-logging.filter'; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +jest.spyOn(Logger.prototype, 'error').mockImplementation(() => {}); + +describe('Error logging filter', () => { + let httpAdapterHost: HttpAdapterHost; + let errorLoggingFilter: ErrorLoggingFilter; + const mockStatus = jest.fn(); + const mockGetResponse = jest.fn().mockImplementation(() => ({ + status: mockStatus, + })); + const mockHttpArgumentsHost = jest.fn().mockImplementation(() => ({ + getResponse: mockGetResponse, + })); + const mockArgumentsHost = { + switchToHttp: mockHttpArgumentsHost, + getArgByIndex: jest.fn(), + getArgs: jest.fn(), + getType: jest.fn(), + switchToRpc: jest.fn(), + switchToWs: jest.fn(), + }; + beforeEach(() => { + httpAdapterHost = new HttpAdapterHost(); + httpAdapterHost.httpAdapter = { + reply: jest.fn(), + } as any; + errorLoggingFilter = new ErrorLoggingFilter(httpAdapterHost); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('returns status 500 and logs the error when it is not a generic HTTP exception', () => { + const err = new Error('Generic'); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith(err); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + err, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + }); + + it('returns the status of the HTTP exception and logs the stringified error', () => { + const err = new HttpException('Exception', HttpStatus.BAD_REQUEST); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith('"Exception"'); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + 'Exception', + HttpStatus.BAD_REQUEST, + ); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/error-logging.filter.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/error-logging.filter.ts new file mode 100644 index 00000000..3371caf5 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/error-logging.filter.ts @@ -0,0 +1,37 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { HttpAdapterHost } from '@nestjs/core'; + +@Catch() +export class ErrorLoggingFilter implements ExceptionFilter { + public logger = new Logger('ErrorLoggingFilter'); + + constructor(private readonly httpAdapterHost: HttpAdapterHost) {} + + public catch(exception: unknown, host: ArgumentsHost): void { + const { httpAdapter } = this.httpAdapterHost; + const ctx = host.switchToHttp(); + + const httpStatus = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + if (exception instanceof HttpException) { + const error = exception.getResponse(); + this.logger.error(JSON.stringify(error)); + + return httpAdapter.reply(ctx.getResponse(), error, httpStatus); + } + + this.logger.error(exception); + + return httpAdapter.reply(ctx.getResponse(), exception, httpStatus); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/error.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/error.spec.ts new file mode 100644 index 00000000..5550dd9a --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/error.spec.ts @@ -0,0 +1,59 @@ +import { ErrorMapping, mapError, rethrowError } from './error'; + +describe('mapError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const newError = new TypeError('type-error'); + + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return newError; + }, + }; + + it('should return the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(mapError(mapping)(error)).toBe(error); + }); + + it('should return the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(mapError(mapping)(error)).toBe(newError); + }); + }); +}); + +describe('rethrowError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return new TypeError('type-error'); + }, + }; + + it('should rethrow the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(() => rethrowError(mapping)(error)).toThrow(error); + }); + + it('should rethrow the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(() => rethrowError(mapping)(error)).toThrow( + new TypeError('type-error'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/error.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/error.ts new file mode 100644 index 00000000..df376a09 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/error.ts @@ -0,0 +1,28 @@ +type IsErrorPredicate = (error: unknown) => boolean; + +type ErrorFactory = () => Error; + +export interface ErrorMapping { + isError: IsErrorPredicate; + newError: ErrorFactory; +} + +export const mapError = function (...mappings: ErrorMapping[]) { + return function (err: Error) { + for (const { isError, newError } of mappings) { + if (isError(err)) { + return newError(); + } + } + + return err; + }; +}; + +export const rethrowError = function (...mappings: ErrorMapping[]) { + const errorMapper = mapError(...mappings); + + return function (err: Error) { + throw errorMapper(err); + }; +}; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/index.ts new file mode 100644 index 00000000..9841f297 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/index.ts @@ -0,0 +1 @@ +export * from './service-to-http-error.interceptor'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/request-logging.interceptor.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/request-logging.interceptor.ts new file mode 100644 index 00000000..b8d9197b --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/request-logging.interceptor.ts @@ -0,0 +1,83 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, + Logger, +} from '@nestjs/common'; +import { catchError, Observable, tap, throwError } from 'rxjs'; + +const REMOVED = '[[REMOVED]]'; + +@Injectable() +export class RequestLoggingInterceptor implements NestInterceptor { + private logger = new Logger('RequestLoggingInterceptor'); + + private sanitizeHeaders(headers: Record): Record { + const sanitizedHeaders = { ...headers }; + + if (sanitizedHeaders.Authorization) { + sanitizedHeaders.Authorization = REMOVED; + } + + if (sanitizedHeaders.Cookie) { + sanitizedHeaders.Cookie = REMOVED; + } + + return sanitizedHeaders; + } + + private sanitizeBody(body: Record): Record { + const sanitizedBody = { ...body }; + + if (sanitizedBody.password) { + sanitizedBody.password = REMOVED; + } + + return sanitizedBody; + } + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const timestamp = new Date().toISOString(); + const startTime = process.hrtime(); + + const { url, body, method, headers, ip } = context + .switchToHttp() + .getRequest(); + + return next.handle().pipe( + tap(() => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + }; + + this.logger.log(JSON.stringify(logMsg)); + }), + catchError((err: any) => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + err, + }; + + this.logger.log(JSON.stringify(logMsg)); + return throwError(() => err); + }), + ); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/service-to-http-error.interceptor.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/service-to-http-error.interceptor.ts new file mode 100644 index 00000000..3e6497c1 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/interceptors/service-to-http-error.interceptor.ts @@ -0,0 +1,31 @@ +import { + CallHandler, + ConflictException, + ExecutionContext, + Injectable, + NestInterceptor, + NotFoundException, +} from '@nestjs/common'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { DuplicateRecordError, RecordNotFoundError } from '@database/errors'; + +const map = { + [RecordNotFoundError.name]: NotFoundException, + [DuplicateRecordError.name]: ConflictException, +}; + +@Injectable() +export class ServiceToHttpErrorsInterceptor implements NestInterceptor { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + catchError((err: Error) => + throwError(() => { + const klass = map[err.name]; + return klass ? new klass(err.message) : err; + }) + ) + ); + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/interfaces/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/interfaces/index.ts new file mode 100644 index 00000000..1e2f37af --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/interfaces/index.ts @@ -0,0 +1,7 @@ +export type IsNullable = null extends T ? K : never; + +export type NullableKeys = { [K in keyof T]: IsNullable }[keyof T]; + +/** `Partial` just for the nullable keys */ +export type NullableKeysPartial = Omit> & + Partial>>; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/index.ts new file mode 100644 index 00000000..26633dc2 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/index.ts @@ -0,0 +1 @@ +export * from './to-object-id'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.spec.ts new file mode 100644 index 00000000..36157487 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.spec.ts @@ -0,0 +1,22 @@ +import { BadRequestException } from '@nestjs/common'; +import { ObjectId } from 'mongodb'; +import { ToObjectIdPipe } from './to-object-id'; + +describe('To ObjectId Pipe', () => { + let pipe: ToObjectIdPipe; + beforeEach(() => { + pipe = new ToObjectIdPipe(); + }); + + it('converts the value to ObjectId when the input is of valid type', () => { + const strObjectId = new ObjectId(); + + expect(pipe.transform(strObjectId)).toEqual(strObjectId); + }); + + it('throws an error when the input is of invalid type', () => { + expect(() => pipe.transform('test')).toThrow( + new BadRequestException('Invalid Id format') + ); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.ts new file mode 100644 index 00000000..9de7aa32 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/pipes/to-object-id.ts @@ -0,0 +1,13 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { ObjectId } from 'mongodb'; + +@Injectable() +export class ToObjectIdPipe implements PipeTransform { + public transform(value: any): ObjectId { + try { + return new ObjectId(value); + } catch (err) { + throw new BadRequestException('Invalid Id format'); + } + } +} diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/query/error.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/query/error.spec.ts new file mode 100644 index 00000000..08fd1ae9 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/query/error.spec.ts @@ -0,0 +1,47 @@ +import { RecordNotFoundError } from '@database/errors'; +import { definedOrNotFound, updatedOrNotFound } from './error'; + +describe('definedOrNotFound', () => { + it('should return a function', () => { + expect(typeof definedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the value it is called with when the value is not undefined', () => { + expect(definedOrNotFound('message')(undefined)).toBe(undefined); + expect(definedOrNotFound('message')(false)).toBe(false); + expect(definedOrNotFound('message')(0)).toBe(0); + expect(definedOrNotFound('message')('')).toBe(''); + const emptyArray: unknown[] = []; + expect(definedOrNotFound('message')(emptyArray)).toBe(emptyArray); + const emptyObject = {}; + expect(definedOrNotFound('message')(emptyObject)).toEqual(emptyObject); + }); + + it('should throw RecordNotFound when the value it is called with is undefined', () => { + expect(() => definedOrNotFound('message')(null)).toThrow( + new RecordNotFoundError('message'), + ); + }); + }); +}); + +describe('updatedOrNotFound', () => { + it('should return a function', () => { + expect(typeof updatedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the number it is called with when the number is not zero', () => { + expect(updatedOrNotFound('message')(-1)).toBe(-1); + expect(updatedOrNotFound('message')(1)).toBe(1); + expect(updatedOrNotFound('message')(NaN)).toEqual(NaN); + }); + + it('should throw RecordNotFound when the value it is called with is zero', () => { + expect(() => updatedOrNotFound('message')(0)).toThrow( + new RecordNotFoundError('message'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/query/error.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/query/error.ts new file mode 100644 index 00000000..56c3448e --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/query/error.ts @@ -0,0 +1,25 @@ +import { RecordNotFoundError } from '@database/errors'; + +const definedOrThrow = (errorFactory: () => Error) => { + return (result: T | null): T => { + if (result === null) { + throw errorFactory(); + } + return result; + }; +}; + +const updatedOrThrow = (errorFactory: () => Error) => { + return (result: number): number => { + if (result === 0) { + throw errorFactory(); + } + return result; + }; +}; + +export const definedOrNotFound = (message: string) => + definedOrThrow(() => new RecordNotFoundError(message)); + +export const updatedOrNotFound = (message: string) => + updatedOrThrow(() => new RecordNotFoundError(message)); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/query/index.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/query/index.ts new file mode 100644 index 00000000..33fe6e52 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/query/index.ts @@ -0,0 +1,4 @@ +export * from './error'; +// export * from './filtration'; +export * from './pagination'; +export * from './sorting'; diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/query/pagination.spec.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/query/pagination.spec.ts new file mode 100644 index 00000000..378c4a4d --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/query/pagination.spec.ts @@ -0,0 +1,24 @@ +import { + Pagination, + extractPagination, + paginationDefaults, +} from './pagination'; + +describe('extractPagination', () => { + it('should return the provided pagination', () => { + const pagination: Pagination = { pageNumber: 1, pageSize: 10 }; + expect(extractPagination(pagination)).toEqual(pagination); + }); + + it('should supply the defaults for any missing pagination properties', () => { + expect(extractPagination()).toEqual(paginationDefaults); + expect(extractPagination({ pageNumber: 1 })).toEqual({ + ...paginationDefaults, + pageNumber: 1, + }); + expect(extractPagination({ pageSize: 10 })).toEqual({ + ...paginationDefaults, + pageSize: 10, + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/query/pagination.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/query/pagination.ts new file mode 100644 index 00000000..a5e5d6e8 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/query/pagination.ts @@ -0,0 +1,24 @@ +export const paginationDefaults = { + pageNumber: 1, + pageSize: 20, +}; + +export interface Pagination { + pageNumber?: number; + pageSize?: number; +} + +interface PaginationResponse { + totalPages: number; + currentPage: number; +} + +export interface Paginated extends PaginationResponse { + items: Entity[]; + total: number; +} + +export const extractPagination = (pagination?: Pagination) => ({ + ...paginationDefaults, + ...pagination, +}); diff --git a/node-cli/assets/nest/example-app-mongodb/src/utils/query/sorting.ts b/node-cli/assets/nest/example-app-mongodb/src/utils/query/sorting.ts new file mode 100644 index 00000000..e9004397 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/src/utils/query/sorting.ts @@ -0,0 +1,9 @@ +export enum SortOrder { + Asc = 'asc', + Desc = 'desc', +} + +export interface Sort { + column: SortColumn; + order?: SortOrder; +} diff --git a/node-cli/assets/nest/example-app-mongodb/test/healthchecks.e2e-spec.ts b/node-cli/assets/nest/example-app-mongodb/test/healthchecks.e2e-spec.ts new file mode 100644 index 00000000..ed67bfa2 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/test/healthchecks.e2e-spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; + +describe('GET /healthz', () => { + let app: NestFastifyApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [HealthchecksModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + + it('/live', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/live', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); + + it('/ready', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/ready', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-mongodb/test/jest-e2e.config.js b/node-cli/assets/nest/example-app-mongodb/test/jest-e2e.config.js new file mode 100644 index 00000000..21834cad --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/test/jest-e2e.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '..', + testEnvironment: 'node', + testRegex: '.e2e-spec.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + coverageDirectory: 'coverage-e2e', + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/.*spec.ts$', + '/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*repository.ts$', + '/src/.*guard.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + '^@test/(.*)$': '/test/$1', + }, +}; diff --git a/node-cli/assets/nest/example-app-mongodb/test/utils/db-setup.ts b/node-cli/assets/nest/example-app-mongodb/test/utils/db-setup.ts new file mode 100644 index 00000000..30ea1183 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/test/utils/db-setup.ts @@ -0,0 +1,56 @@ +import { Collections } from '@database/constants'; +import { Db, ObjectId } from 'mongodb'; + +export class MongoDBTestSetup { + public readonly userId: ObjectId; + + constructor(private readonly mongodb: Db) { + this.userId = new ObjectId(); + this.mongodb = mongodb; + } + + async seedData() { + await this.mongodb.createCollection(Collections.Users); + await this.mongodb.createCollection(Collections.Todos); + await this.mongodb.createIndex(Collections.Todos, 'user_id'); + await seedDb(this.mongodb, this.userId); + } + + async removeSeededData() { + await this.mongodb.dropCollection(Collections.Todos); + await this.mongodb.dropCollection(Collections.Users); + } +} + +async function seedDb(mongodb: Db, userId: ObjectId) { + await mongodb.collection('users').deleteMany(); + await mongodb.collection('users').insertOne( + // The original password for this hash is 'pass@ord' + { + email: 'hello@email.com', + password: '$2b$10$Mxur7NOiTlm22yuldEMZgOCbIV7bxDCcUbBLFbzrJ1MrnIczZB.92', // pragma: allowlist secret + userId, + }, + ); + await mongodb.collection('todos').deleteMany(); + await mongodb.collection('todos').insertMany([ + { + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId, + }, + { + name: 'Laundry 2', + note: 'Buy detergent 2', + completed: false, + userId, + }, + { + name: 'Laundry 3', + note: 'Buy detergent 3', + completed: true, + userId, + }, + ]); +} diff --git a/node-cli/assets/nest/example-app-mongodb/test/utils/expect-error.ts b/node-cli/assets/nest/example-app-mongodb/test/utils/expect-error.ts new file mode 100644 index 00000000..6837da87 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/test/utils/expect-error.ts @@ -0,0 +1,11 @@ +import { HttpException } from '@nestjs/common'; + +export const expectError = async ( + ex: T, + jsonResponse: Response['json'] +) => { + const response = await jsonResponse(); + + expect(ex.message).toEqual(response.error || response.message); + expect(ex.getStatus()).toEqual(response.statusCode); +}; diff --git a/node-cli/assets/nest/example-app-mongodb/test/utils/index.ts b/node-cli/assets/nest/example-app-mongodb/test/utils/index.ts new file mode 100644 index 00000000..cad2ae0f --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/test/utils/index.ts @@ -0,0 +1,2 @@ +export * from './expect-error'; +export * from './db-setup'; diff --git a/node-cli/assets/nest/example-app-mongodb/tsconfig.build.json b/node-cli/assets/nest/example-app-mongodb/tsconfig.build.json new file mode 100644 index 00000000..f54d2265 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + "test", + "dist", + "**/*spec.ts", + "**/__mocks__" + ] +} diff --git a/node-cli/assets/nest/example-app-mongodb/tsconfig.json b/node-cli/assets/nest/example-app-mongodb/tsconfig.json new file mode 100644 index 00000000..b31a3ff6 --- /dev/null +++ b/node-cli/assets/nest/example-app-mongodb/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "paths": { + "@api/*": ["src/api/*"], + "@database": ["src/database"], + "@database/*": ["src/database/*"], + "@extensions/*": ["src/extensions/*"], + "@middleware": ["src/middleware"], + "@utils/*": ["src/utils/*"], + "@test/*": ["test/*"] + } + }, + "ts-node": { + "require": ["tsconfig-paths/register", "dotenv/config"] + } +} diff --git a/node-cli/assets/nest/example-app-pg/.eslintrc.js b/node-cli/assets/nest/example-app-pg/.eslintrc.js new file mode 100644 index 00000000..259de13c --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + 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/assets/nest/example-app-pg/.gitignore b/node-cli/assets/nest/example-app-pg/.gitignore similarity index 100% rename from assets/nest/example-app-pg/.gitignore rename to node-cli/assets/nest/example-app-pg/.gitignore diff --git a/node-cli/assets/nest/example-app-pg/.ls-lint.yml b/node-cli/assets/nest/example-app-pg/.ls-lint.yml new file mode 100644 index 00000000..024d598f --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/.ls-lint.yml @@ -0,0 +1,30 @@ +ls: + # directories + .dir: kebab-case | regex:__tests__ | regex:__mocks__ | regex:__data__ + + # shell scripts + .sh: kebab-case + + # source files + # this is essentially kebab-case.with-custom.extentions.js + .js: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + .ts: regex:([a-z]+)([a-z0-9]*)([-.][a-z0-9]+)* + + # migrations + migrations: + .ts: snake_case + +# ignored directories and files +ignore: + - node_modules + - .git + - .github + - .vscode + - .husky + - dist + - .commitlintrc.js + - .eslintrc.js + - .prettierrc.js + - .openapi + - src/@types + - .devcontainer diff --git a/node-cli/assets/nest/example-app-pg/.openapi/gitignorefile b/node-cli/assets/nest/example-app-pg/.openapi/gitignorefile new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/.openapi/gitignorefile @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/node-cli/assets/nest/example-app-pg/docker-compose.override.example.yml b/node-cli/assets/nest/example-app-pg/docker-compose.override.example.yml new file mode 100644 index 00000000..f2ec79b4 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/docker-compose.override.example.yml @@ -0,0 +1,6 @@ +services: + + # uncomment the lines below and adjust the volume if desired + # db: + # volumes: + # - /path/to/volume:/var/lib/postgresql/data diff --git a/node-cli/assets/nest/example-app-pg/docker-compose.yml b/node-cli/assets/nest/example-app-pg/docker-compose.yml new file mode 100644 index 00000000..a573fc00 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/docker-compose.yml @@ -0,0 +1,47 @@ +version: '3' + +services: + db: + image: postgres:13-alpine + container_name: postgres + restart: always + ports: + - '5432:5432' + environment: + POSTGRES_USER: ${PGUSER} + POSTGRES_DB: ${PGDATABASE} + POSTGRES_PASSWORD: ${PGPASSWORD} + volumes: + - db-data:/var/lib/postgresql/data + + api: + build: + context: . + dockerfile: Dockerfile + target: dev + container_name: todo-api + image: todo-api-dev + ports: + - '${PORT}:${PORT}' + env_file: + - .env + # Mount host directory to docker container to support watch mode + volumes: + - .:/usr/src/app + # This ensures that the NestJS container manages the node_modules folder + # rather than synchronizes it with the host machine + - api-node_modules:/usr/src/app/node_modules + depends_on: + - db + command: > + sh -c "cd /usr/src/app + && npm run db:migrate:latest + && npm run start:dev" + +volumes: + api-node_modules: + db-data: + +networks: + default: + name: nest-api diff --git a/node-cli/assets/nest/example-app-pg/jest.config.js b/node-cli/assets/nest/example-app-pg/jest.config.js new file mode 100644 index 00000000..575c1728 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/jest.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + collectCoverageFrom: ['/src/**/*.(t|j)s'], + coverageDirectory: './coverage', + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/src/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*controller.ts$', + '/src/utils/class-transformers/.*', + '/src/database/seeds/.*', + '/src/.*interceptor.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '.', + testEnvironment: 'node', + testRegex: '.*\\.spec\\.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, +}; diff --git a/node-cli/assets/nest/example-app-pg/jest.setup.ts b/node-cli/assets/nest/example-app-pg/jest.setup.ts new file mode 100644 index 00000000..4134a28a --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/jest.setup.ts @@ -0,0 +1,7 @@ +import '@database/extensions/knex/register'; +import 'reflect-metadata'; + +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/node-cli/assets/nest/example-app-pg/migrations/20230302155244_initial.ts b/node-cli/assets/nest/example-app-pg/migrations/20230302155244_initial.ts new file mode 100644 index 00000000..e4058ea4 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/migrations/20230302155244_initial.ts @@ -0,0 +1,10 @@ +import { Knex } from 'knex'; +import { createUpdateTimestampsFunctionSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.raw(createUpdateTimestampsFunctionSQL); +} + +export async function down(knex: Knex): Promise { + await knex.raw('DROP FUNCTION IF EXISTS update_timestamp() CASCADE;'); +} diff --git a/node-cli/assets/nest/example-app-pg/migrations/20230302162022_create_table_users.ts b/node-cli/assets/nest/example-app-pg/migrations/20230302162022_create_table_users.ts new file mode 100644 index 00000000..b3987697 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/migrations/20230302162022_create_table_users.ts @@ -0,0 +1,25 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('users', (table) => { + table.bigIncrements('id'); + table + .string('userId') + .unique({ indexName: 'unq_users_user_id' }) + .index(); + table + .string('email') + .notNullable() + .unique({ indexName: 'unq_users_email' }); + table.string('password'); + table.timestamps(false, true, true); + }); + + await knex.raw(createUpdatedAtTriggerSQL('users')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('users')); + await knex.schema.dropTable('users'); +} diff --git a/node-cli/assets/nest/example-app-pg/migrations/20230302162023_create_table_todos.ts b/node-cli/assets/nest/example-app-pg/migrations/20230302162023_create_table_todos.ts new file mode 100644 index 00000000..67fedbcb --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/migrations/20230302162023_create_table_todos.ts @@ -0,0 +1,28 @@ +import { Knex } from 'knex'; +import { createUpdatedAtTriggerSQL, dropUpdatedAtTriggerSQL } from './utils'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('todos', (table) => { + table.bigIncrements('id'); + table.string('name').notNullable(); + table.text('note'); + table.boolean('completed').notNullable().defaultTo(false); + table.timestamps(false, true, true); + table.string('userId'); + table + .foreign('userId', 'fk_todos_user_id') + .references('userId') + .inTable('users') + .onDelete('CASCADE'); + table.index('name', 'idx_todos_name'); + table.index('userId', 'idx_todos_userId'); + table.index('createdAt', 'idx_todos_created_at'); + }); + + await knex.raw(createUpdatedAtTriggerSQL('todos')); +} + +export async function down(knex: Knex): Promise { + await knex.raw(dropUpdatedAtTriggerSQL('todos')); + await knex.schema.dropTable('todos'); +} diff --git a/node-cli/assets/nest/example-app-pg/migrations/utils/index.ts b/node-cli/assets/nest/example-app-pg/migrations/utils/index.ts new file mode 100644 index 00000000..bbec689b --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/migrations/utils/index.ts @@ -0,0 +1,23 @@ +export const createUpdateTimestampsFunctionSQL = ` +CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER +LANGUAGE plpgsql +AS +$$ +BEGIN + NEW."updatedAt" = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$; +`; + +export const createUpdatedAtTriggerSQL = (tableName: string) => ` +CREATE TRIGGER update_timestamp +BEFORE UPDATE +ON ${tableName} +FOR EACH ROW +EXECUTE PROCEDURE update_timestamp(); +`; + +export const dropUpdatedAtTriggerSQL = (tableName: string) => ` +DROP TRIGGER update_timestamp ON ${tableName}; +`; diff --git a/node-cli/assets/nest/example-app-pg/nest-cli.json b/node-cli/assets/nest/example-app-pg/nest-cli.json new file mode 100644 index 00000000..f9aa683b --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.controller.ts b/node-cli/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.controller.ts new file mode 100644 index 00000000..d898fd58 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.controller.ts @@ -0,0 +1,50 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '@utils/decorators'; + +@ApiTags('Healthchecks') +@Controller('healthz') +export class HealthchecksController { + @Public() + @Get('live') + @ApiOperation({ + summary: 'Liveness endpoint', + description: 'Used to check whether the application is alive.', + }) + @ApiOkResponse({ + description: 'Application is alive', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + live(): string { + return 'OK'; + } + + @Public() + @Get('ready') + @ApiOperation({ + summary: 'Readiness endpoint', + description: + 'Used to check whether the application is ready to receive requests.', + }) + @ApiOkResponse({ + description: 'Application is ready', + content: { + 'text/plain': { + schema: { + type: 'string', + example: 'OK', + }, + }, + }, + }) + ready(): string { + return 'OK'; + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.module.ts b/node-cli/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.module.ts new file mode 100644 index 00000000..797561fc --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/healthchecks/healthchecks.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HealthchecksController } from './healthchecks.controller'; + +@Module({ + controllers: [HealthchecksController], +}) +export class HealthchecksModule {} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/__mocks__/index.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/__mocks__/index.ts new file mode 100644 index 00000000..3f42ef2e --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/__mocks__/index.ts @@ -0,0 +1 @@ +export * from './todos.mocks'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/__mocks__/todos.mocks.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/__mocks__/todos.mocks.ts new file mode 100644 index 00000000..1ac8f0f0 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/__mocks__/todos.mocks.ts @@ -0,0 +1,73 @@ +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { Paginated } from '@utils/query'; +import { Todo } from '../entities'; +import { TodosSortBy, UpdateTodoDto } from '../dto'; +import { UserData } from '@api/auth/interfaces'; + +export const mockedUser: UserData = { + user: { + sub: 'tz4a98xxat96iws9zmbrgj3a', + email: 'example@email.com', + }, +}; + +const userId = mockedUser.user.sub; + +export const todo: Todo = { + id: 1, + name: 'todo', + note: null, + completed: false, + createdAt: '2023-09-01T09:44:15.515Z', + updatedAt: '2023-09-01T09:44:15.515Z', + userId, +}; + +export const createTodoInput: CreateTodoInput = { + createTodoDto: { + name: todo.name, + completed: todo.completed, + note: todo.note, + }, + userId, +}; + +export const updateTodoDtoInput: UpdateTodoDto = { + name: 'new name', + note: 'new note', + completed: true, +}; + +export const updateTodoInput: UpdateTodoInput = { + id: todo.id, + updateTodoDto: updateTodoDtoInput, + userId, +}; + +export const findOneTodoInput: FindOneTodoInput = { + id: todo.id, + userId, +}; + +export const findAllTodosInput: FindAllTodosInput = { + query: { + pageNumber: 1, + pageSize: 20, + column: TodosSortBy.CreatedAt, + }, + userId, +}; + +export const getPaginatedResponse = (data: T[]): Paginated => { + return { + items: data, + total: data.length, + totalPages: 1, + currentPage: 1, + }; +}; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/dto/create-todo.dto.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/create-todo.dto.ts new file mode 100644 index 00000000..c100f414 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/create-todo.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Trim } from '@utils/class-transformers'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; + +export class CreateTodoDto { + @ApiProperty() + @Trim() + @MinLength(1) + @MaxLength(255) + name: string; + + @ApiProperty({ type: String, required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/dto/find-all-todos-query.dto.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/find-all-todos-query.dto.ts new file mode 100644 index 00000000..9fe32c7a --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/find-all-todos-query.dto.ts @@ -0,0 +1,75 @@ +import { SortOrder } from '@utils/query'; +import { + Allow, + IsEnum, + IsNumber, + IsOptional, + MaxLength, + MinLength, +} from 'class-validator'; +import { Transform } from 'class-transformer'; +import { Trim } from '@utils/class-transformers'; +import { ApiProperty } from '@nestjs/swagger'; + +export enum TodosSortBy { + Name = 'name', + CreatedAt = 'createdAt', +} + +export class FindAllTodosQueryDto { + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ + type: Boolean, + required: false, + }) + @Transform(({ value }) => + value === 'true' ? true : value === 'false' ? false : undefined, + ) + @IsOptional() + completed?: boolean; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @Trim() + @IsEnum(TodosSortBy) + column?: TodosSortBy; + + @ApiProperty({ + type: String, + required: false, + }) + @IsOptional() + @IsEnum(SortOrder) + @Allow(undefined) + order?: SortOrder | undefined; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number = 1; + + @ApiProperty({ + type: Number, + required: false, + }) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number = 20; +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/dto/index.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/index.ts new file mode 100644 index 00000000..ea59f55f --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-todo.dto'; +export * from './find-all-todos-query.dto'; +export * from './update-todo.dto'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/dto/update-todo.dto.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/update-todo.dto.ts new file mode 100644 index 00000000..e00f0533 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/dto/update-todo.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateTodoDto } from './create-todo.dto'; +import { IsBoolean, IsOptional, MaxLength, MinLength } from 'class-validator'; +import { Trim } from '@utils/class-transformers'; + +export class UpdateTodoDto extends PartialType(CreateTodoDto) { + @ApiProperty({ required: false }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + name?: string; + + @ApiProperty({ required: false, nullable: true }) + @IsOptional() + @Trim() + @MinLength(1) + @MaxLength(255) + note?: string | null; + + @ApiProperty({ required: false }) + @IsOptional() + @IsBoolean() + completed?: boolean; +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/entities/index.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/entities/index.ts new file mode 100644 index 00000000..69b0759f --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/entities/index.ts @@ -0,0 +1 @@ +export * from './todo.entity'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/entities/todo.entity.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/entities/todo.entity.ts new file mode 100644 index 00000000..2d92e95a --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/entities/todo.entity.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { GenericEntity } from '@utils/entities/generic-entity'; + +export class Todo extends GenericEntity { + @ApiProperty() + userId: string; + + @ApiProperty() + name: string; + + @ApiProperty({ nullable: true }) + note: string | null; + + @ApiProperty({ default: false }) + completed = false; +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/error-mappings/index.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/error-mappings/index.ts new file mode 100644 index 00000000..cec33482 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './todo-user-not-found.error-mapping'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts new file mode 100644 index 00000000..63ccc517 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/error-mappings/todo-user-not-found.error-mapping.ts @@ -0,0 +1,6 @@ +import { foreignKeyViolation } from '@database/error-mappings'; + +export const TodoUserNotFound = foreignKeyViolation( + 'fk_todos_user_id', + 'User not found', +); diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/interfaces/index.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/interfaces/index.ts new file mode 100644 index 00000000..1faae003 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/interfaces/index.ts @@ -0,0 +1 @@ +export * from './todos.interface'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/interfaces/todos.interface.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/interfaces/todos.interface.ts new file mode 100644 index 00000000..7ce97c70 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/interfaces/todos.interface.ts @@ -0,0 +1,23 @@ +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from '../dto'; + +interface UserData { + userId: string; +} + +interface TodoId { + id?: number; +} + +export interface CreateTodoInput extends UserData { + createTodoDto: CreateTodoDto; +} + +export interface FindAllTodosInput extends UserData { + query: FindAllTodosQueryDto; +} + +export interface FindOneTodoInput extends UserData, TodoId, UpdateTodoDto {} + +export interface UpdateTodoInput extends FindOneTodoInput { + updateTodoDto: UpdateTodoDto; +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/index.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/index.ts new file mode 100644 index 00000000..2639d438 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/index.ts @@ -0,0 +1 @@ +export * from './todos.repository'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.spec.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.spec.ts new file mode 100644 index 00000000..6a5521e3 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.spec.ts @@ -0,0 +1,142 @@ +import { Test } from '@nestjs/testing'; +import { NestKnexService } from '@database/nest-knex.service'; +import { TodosRepository } from './todos.repository'; +import { + createTodoInput, + findAllTodosInput, + findOneTodoInput, + mockedUser, + todo, + updateTodoInput, +} from '../__mocks__'; + +describe('TodosRepository', () => { + let todosRepository: TodosRepository; + + const first = jest.fn((): Promise => Promise.resolve()); + const del = jest.fn(() => Promise.resolve({})); + const returning = jest.fn().mockImplementation(() => Promise.resolve([])); + const paginate = jest.fn(() => Promise.resolve({})); + + const sort = jest.fn().mockImplementation(() => ({ + paginate, + })); + + const filter = jest.fn().mockImplementation(() => ({ + clone, + })); + + const clone = jest.fn().mockImplementation(() => ({ + sort, + paginate, + })); + + const where = jest.fn().mockImplementation(() => ({ + first, + update, + del, + filter, + })); + + const insert = jest.fn().mockImplementation(() => ({ + returning, + })); + + const update = jest.fn().mockImplementation(() => ({ + returning, + })); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: NestKnexService, + useFactory: () => ({ + connection: () => ({ + insert, + where, + }), + }), + }, + TodosRepository, + ], + }).compile(); + + todosRepository = moduleRef.get(TodosRepository); + }); + + it('create - create a todo', async () => { + returning.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.create(createTodoInput); + + expect(result).toBe(todo); + expect(insert).toHaveBeenCalledWith({ + ...createTodoInput.createTodoDto, + userId: createTodoInput.userId, + }); + }); + + it('findOne - find a todo', async () => { + first.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOne(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(where).toHaveBeenCalledWith(findOneTodoInput); + }); + + it('findOneOrFail - find a todo', async () => { + first.mockImplementationOnce(() => Promise.resolve([todo])); + + const result = await todosRepository.findOneOrFail(findOneTodoInput); + + expect(result).toStrictEqual([todo]); + expect(where).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('update', () => { + it('update - update a todo', async () => { + const updatedTodo = { + ...todo, + name: updateTodoInput.updateTodoDto.name, + }; + + returning.mockImplementationOnce(() => Promise.resolve([updatedTodo])); + + const result = await todosRepository.update(updateTodoInput); + + expect(result).toStrictEqual(updatedTodo); + expect(where).toHaveBeenCalledWith({ + id: updateTodoInput.id, + userId: mockedUser.user.sub, + }); + expect(update).toHaveBeenCalledWith(updateTodoInput.updateTodoDto); + }); + }); + + it('remove - delete a todo', async () => { + del.mockImplementationOnce(() => Promise.resolve(1)); + + const result = await todosRepository.remove(findOneTodoInput); + + expect(result).toBe(1); + expect(where).toHaveBeenCalledWith(findOneTodoInput); + }); + + describe('findAll', () => { + it('find all todos for the user', async () => { + paginate.mockImplementationOnce(() => Promise.resolve([todo])); + jest.spyOn(todosRepository, 'count').mockResolvedValueOnce(1); + + const result = await todosRepository.findAll(findAllTodosInput); + + expect(result).toStrictEqual({ + items: [todo], + total: 1, + currentPage: 1, + totalPages: 1, + }); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.ts new file mode 100644 index 00000000..bef151ca --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/repositories/todos.repository.ts @@ -0,0 +1,92 @@ +import { rethrowError } from '@utils/error'; +import { NestKnexService } from '@database/nest-knex.service'; +import { Paginated } from '@utils/query/pagination'; +import { Todo } from '../entities'; +import { Injectable } from '@nestjs/common'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from '../interfaces/todos.interface'; +import { definedOrNotFound } from '@utils/query'; +import { TodoUserNotFound } from '../error-mappings'; +import { paginatedResponse } from '@utils/api/response'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; +import { Errors } from '@utils/enums'; + +@Injectable() +export class TodosRepository extends BaseRepository { + constructor(private readonly knex: NestKnexService) { + super(knex, Tables.Todos); + } + + async create(input: CreateTodoInput): Promise { + return this.repository() + .insert({ ...input.createTodoDto, userId: input.userId }) + .returning('*') + .then(([todo]: Todo[]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async findOne(input: Partial): Promise { + return this.repository() + .where({ ...input }) + .first(); + } + + async findOneOrFail(input: Partial): Promise { + return this.repository() + .where({ ...input }) + .first() + .then(definedOrNotFound(Errors.NotFound)); + } + + async update(input: UpdateTodoInput): Promise { + const { id, userId, updateTodoDto } = input; + + return this.repository() + .where({ id, userId }) + .update(updateTodoDto) + .returning('*') + .then(([todo]: Todo[]) => todo) + .catch(rethrowError(TodoUserNotFound)); + } + + async remove(input: FindOneTodoInput): Promise { + return this.repository() + .where({ ...input }) + .del(); + } + + async findAll(input: FindAllTodosInput): Promise> { + const { + userId, + query: { pageNumber, pageSize, name, completed, column, order }, + } = input; + + const pagination = { pageNumber, pageSize }; + const filters = { name, completed }; + + const qb = this.repository().where({ userId }).filter(filters, { + name: this.whereLike, + completed: this.where, + }); + + const itemsQuery = qb.clone(); + + if (column) { + itemsQuery.sort([{ column, order }], { + name: this.orderBy, + createdAt: this.orderBy, + }); + } + + const items = await itemsQuery.paginate(pagination); + + const count = await this.count(qb); + + return paginatedResponse(items, count, pagination); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/todos.controller.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.controller.ts new file mode 100644 index 00000000..f1a5f78b --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.controller.ts @@ -0,0 +1,106 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Delete, + Req, + ParseIntPipe, + Put, + Query, +} from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { + ApiBadRequestResponse, + ApiBody, + ApiCreatedResponse, + ApiNotFoundResponse, + ApiOkResponse, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { Todo } from './entities'; +import { Paginated } from '@utils/query'; +import { CreateTodoDto, FindAllTodosQueryDto, UpdateTodoDto } from './dto'; +import { UserData } from '@api/auth/interfaces'; +import { + BadRequestDto, + NotFoundDto, + UnprocessableEntityDto, +} from '@utils/dtos'; +import { Errors } from '@utils/enums'; + +@ApiTags('Todos') +@Controller('v1/todos') +export class TodosController { + constructor(private readonly todosService: TodosService) {} + + @ApiBody({ type: CreateTodoDto }) + @ApiCreatedResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @ApiUnprocessableEntityResponse({ + type: UnprocessableEntityDto, + description: Errors.UnprocessableEntity, + }) + @Post() + create( + @Body() createTodoDto: CreateTodoDto, + @Req() { user: { sub } }: UserData, + ): Promise { + return this.todosService.create({ createTodoDto, userId: sub }); + } + + @ApiOkResponse({ type: Todo, isArray: true }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @Get() + findAll( + @Req() { user: { sub } }: UserData, + @Query() query: FindAllTodosQueryDto, + ): Promise> { + return this.todosService.findAll({ userId: sub, query }); + } + + @ApiOkResponse({ type: Todo }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Get(':id') + findOne( + @Param('id', ParseIntPipe) id: number, + @Req() { user: { sub } }: UserData, + ): Promise { + return this.todosService.findOneOrFail({ id, userId: sub }); + } + + @ApiBody({ type: UpdateTodoDto }) + @ApiOkResponse({ type: Todo }) + @ApiBadRequestResponse({ + type: BadRequestDto, + description: Errors.BadRequest, + }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Put(':id') + update( + @Param('id', ParseIntPipe) id: number, + @Req() { user: { sub } }: UserData, + @Body() updateTodoDto: UpdateTodoDto, + ): Promise { + return this.todosService.update({ id, userId: sub, updateTodoDto }); + } + + @ApiOkResponse({ type: Number }) + @ApiNotFoundResponse({ type: NotFoundDto, description: Errors.NotFound }) + @Delete(':id') + remove( + @Param('id', ParseIntPipe) id: number, + @Req() { user: { sub } }: UserData, + ): Promise { + return this.todosService.remove({ id, userId: sub }); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/todos.module.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.module.ts new file mode 100644 index 00000000..20fefaa8 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { DatabaseModule } from '@database/database.module'; +import { TodosRepository } from './repositories/todos.repository'; + +@Module({ + imports: [DatabaseModule], + controllers: [TodosController], + providers: [TodosService, TodosRepository], +}) +export class TodosModule {} diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/todos.service.spec.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.service.spec.ts new file mode 100644 index 00000000..954077de --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.service.spec.ts @@ -0,0 +1,163 @@ +import { DatabaseModule } from '@database/database.module'; +import { Test, TestingModule } from '@nestjs/testing'; +import { TodosService } from './todos.service'; +import { TodosController } from './todos.controller'; +import { TodosRepository } from './repositories'; +import { + createTodoInput, + mockedUser, + getPaginatedResponse, + todo, + updateTodoDtoInput, +} from './__mocks__'; +import { + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { Errors } from '@utils/enums'; +import { dbConfig, nodeConfig } from '@utils/environment'; +import { ConfigModule } from '@nestjs/config'; + +describe('TodosService', () => { + let service: TodosService; + let repository: TodosRepository; + const userId = mockedUser.user.sub; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + DatabaseModule, + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + controllers: [TodosController], + providers: [TodosService, TodosRepository], + }).compile(); + + service = module.get(TodosService); + repository = module.get(TodosRepository); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('create', () => { + it('should return created todo', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => undefined); + jest.spyOn(repository, 'create').mockImplementationOnce(async () => todo); + + expect(await service.create(createTodoInput)).toStrictEqual(todo); + }); + + it('should throw error if todo with the same name exists', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => todo); + + await expect(service.create(createTodoInput)).rejects.toThrowError( + new UnprocessableEntityException(Errors.UnprocessableEntity), + ); + }); + }); + + describe('findAll', () => { + it('should return todos', async () => { + const paginatedResponse = getPaginatedResponse([todo]); + + jest + .spyOn(repository, 'findAll') + .mockImplementationOnce(async () => paginatedResponse); + + expect(await service.findAll({ userId, query: {} })).toStrictEqual( + paginatedResponse, + ); + }); + }); + + describe('findOne', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOne') + .mockImplementationOnce(async () => todo); + + expect(await service.findOne({ id: todo.id, userId })).toStrictEqual( + todo, + ); + }); + }); + + describe('findOneOrFail', () => { + it('should return single todo', async () => { + jest + .spyOn(repository, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.findOneOrFail({ id: todo.id, userId }), + ).toStrictEqual(todo); + }); + }); + + describe('update', () => { + it('should update single todo', async () => { + const updatedTodo = { ...todo, ...updateTodoDtoInput }; + + jest + .spyOn(repository, 'update') + .mockImplementationOnce(async () => updatedTodo); + + expect( + await service.update({ + id: todo.id, + userId, + updateTodoDto: updateTodoDtoInput, + }), + ).toStrictEqual(updatedTodo); + }); + + it('should return todo if there is no data to update', async () => { + jest + .spyOn(service, 'findOneOrFail') + .mockImplementationOnce(async () => todo); + + expect( + await service.update({ + id: todo.id, + userId, + updateTodoDto: {}, + }), + ).toStrictEqual(todo); + }); + }); + + describe('remove', () => { + it('should delete single todo', async () => { + jest.spyOn(service, 'findOne').mockImplementationOnce(async () => todo); + jest.spyOn(repository, 'remove').mockImplementationOnce(async () => 1); + + expect(await service.remove({ id: 1, userId })).toBe(1); + }); + + it('should throw error if todo does not exist', async () => { + jest + .spyOn(service, 'findOne') + .mockImplementationOnce(async () => undefined); + + await expect(service.remove({ id: 1, userId })).rejects.toThrowError( + new NotFoundException(Errors.NotFound), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/api/todos/todos.service.ts b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.service.ts new file mode 100644 index 00000000..04651a9d --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/todos/todos.service.ts @@ -0,0 +1,70 @@ +import { + Inject, + Injectable, + NotFoundException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { TodosRepository } from './repositories'; +import { Paginated, definedOrNotFound } from '@utils/query'; +import { Todo } from './entities/todo.entity'; +import { + CreateTodoInput, + FindAllTodosInput, + FindOneTodoInput, + UpdateTodoInput, +} from './interfaces'; +import { Errors } from '@utils/enums'; + +@Injectable() +export class TodosService { + constructor( + @Inject(TodosRepository) + private readonly todos: TodosRepository, + ) {} + + async create(input: CreateTodoInput): Promise { + const { + userId, + createTodoDto: { name }, + } = input; + + const todo = await this.todos.findOne({ userId, name }); + + if (todo) { + throw new UnprocessableEntityException(Errors.UnprocessableEntity); + } + + return this.todos.create(input); + } + + findAll(input: FindAllTodosInput): Promise> { + return this.todos.findAll(input); + } + + findOne(input: Partial): Promise { + return this.todos.findOne(input); + } + + findOneOrFail(input: Partial): Promise { + return this.todos.findOneOrFail(input); + } + + update(input: UpdateTodoInput): Promise { + const { id, userId, updateTodoDto } = input; + + if (Object.keys(updateTodoDto).length === 0) { + return this.findOneOrFail({ id, userId }); + } + + return this.todos.update(input).then(definedOrNotFound(Errors.NotFound)); + } + + async remove(input: FindOneTodoInput): Promise { + const todo = await this.findOne(input); + if (!todo) { + throw new NotFoundException(Errors.NotFound); + } + + return this.todos.remove(input); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/entities/index.ts b/node-cli/assets/nest/example-app-pg/src/api/users/entities/index.ts new file mode 100644 index 00000000..e4aa5074 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/entities/index.ts @@ -0,0 +1 @@ +export * from './user.entity'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/entities/user.entity.ts b/node-cli/assets/nest/example-app-pg/src/api/users/entities/user.entity.ts new file mode 100644 index 00000000..6f0d6bcb --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/entities/user.entity.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { GenericEntity } from '@utils/entities'; + +export class User extends GenericEntity { + @ApiProperty() + email: string; + + @ApiProperty() + password?: string | null; + + @ApiProperty() + userId: string; +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/error-mappings/index.ts b/node-cli/assets/nest/example-app-pg/src/api/users/error-mappings/index.ts new file mode 100644 index 00000000..9e6f56e6 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './user-email-taken.error-mapping'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/error-mappings/user-email-taken.error-mapping.ts b/node-cli/assets/nest/example-app-pg/src/api/users/error-mappings/user-email-taken.error-mapping.ts new file mode 100644 index 00000000..adfebd97 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/error-mappings/user-email-taken.error-mapping.ts @@ -0,0 +1,6 @@ +import { uniqueViolation } from '@database/error-mappings'; + +export const UserEmailTaken = uniqueViolation( + 'unq_users_email', + 'User email already taken', +); diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/repositories/index.ts b/node-cli/assets/nest/example-app-pg/src/api/users/repositories/index.ts new file mode 100644 index 00000000..96798590 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/repositories/index.ts @@ -0,0 +1 @@ +export * from './users.repository'; diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/repositories/users.repository.spec.ts b/node-cli/assets/nest/example-app-pg/src/api/users/repositories/users.repository.spec.ts new file mode 100644 index 00000000..1fe001dd --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/repositories/users.repository.spec.ts @@ -0,0 +1,107 @@ +import { UsersRepository } from './users.repository'; +import { Test } from '@nestjs/testing'; +import { NestKnexService } from '@database/nest-knex.service'; +import { Credentials } from '@api/auth/interfaces'; + +describe('UsersRepository', () => { + let usersRepository: UsersRepository; + + const first = jest.fn(() => Promise.resolve({})); + const returning = jest.fn().mockImplementation(() => Promise.resolve([])); + + const where = jest.fn().mockImplementation(() => ({ + first, + update, + })); + + const insert = jest.fn().mockImplementation(() => ({ + returning, + })); + + const update = jest.fn().mockImplementation(() => ({ + returning, + })); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: NestKnexService, + useFactory: () => ({ + connection: () => ({ + insert, + where, + update, + }), + }), + }, + UsersRepository, + ], + }).compile(); + + usersRepository = moduleRef.get(UsersRepository); + }); + + it('insertOne - create a user', async () => { + const insertUser: Credentials = { + email: 'user@example.com', + password: 'password', + }; + + const createdUser = { + id: 1, + email: 'user@example.com', + password: 'password', + }; + + returning.mockImplementationOnce(() => Promise.resolve([createdUser])); + + const result = await usersRepository.insertOne({ + email: insertUser.email, + password: insertUser.password, + }); + + expect(result).toBe(createdUser); + expect(insert).toHaveBeenCalledWith({ + email: insertUser.email, + password: insertUser.password, + }); + }); + + it('findByEmail - find a user', async () => { + const userFound = { + id: 1, + email: 'user@example.com', + password: 'password', + }; + + first.mockImplementationOnce(() => Promise.resolve(userFound)); + + const result = await usersRepository.findByEmail('user@example.com'); + + expect(result).toBe(userFound); + expect(where).toHaveBeenCalledWith({ email: 'user@example.com' }); + }); + + it('updateOne - modify a user', async () => { + const updatedUser = { + id: 1, + email: 'user@example.com', + password: 'new-password', + }; + + returning.mockImplementationOnce(() => Promise.resolve([updatedUser])); + + const result = await usersRepository.updateOne(1, { + email: updatedUser.email, + password: updatedUser.password, + }); + + expect(result).toBe(updatedUser); + expect(where).toHaveBeenCalledWith({ id: 1 }); + expect(update).toHaveBeenCalledWith({ + email: updatedUser.email, + password: updatedUser.password, + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/repositories/users.repository.ts b/node-cli/assets/nest/example-app-pg/src/api/users/repositories/users.repository.ts new file mode 100644 index 00000000..583af06a --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/repositories/users.repository.ts @@ -0,0 +1,34 @@ +import { rethrowError } from '@utils/error'; +import { User } from '../entities'; +import { UserEmailTaken } from '../error-mappings'; +import { Injectable } from '@nestjs/common'; +import { NestKnexService } from '@database/nest-knex.service'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; + +@Injectable() +export class UsersRepository extends BaseRepository { + constructor(private readonly knex: NestKnexService) { + super(knex, Tables.Users); + } + + insertOne(payload: Partial): Promise { + return this.repository() + .insert(payload) + .returning('*') + .then((data: any) => data[0]) + .catch(rethrowError(UserEmailTaken)); + } + + findByEmail(email: User['email']): Promise { + return this.repository().where({ email }).first(); + } + + updateOne(id: number, payload: Partial): Promise { + return this.repository() + .where({ id }) + .update(payload) + .returning('*') + .then((data: any) => data[0]) + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/api/users/users.module.ts b/node-cli/assets/nest/example-app-pg/src/api/users/users.module.ts new file mode 100644 index 00000000..1ce5ac8b --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/api/users/users.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { UsersRepository } from './repositories'; +import { DatabaseModule } from '@database/database.module'; + +@Module({ + imports: [DatabaseModule], + providers: [UsersRepository], + exports: [UsersRepository], +}) +export class UsersModule {} diff --git a/node-cli/assets/nest/example-app-pg/src/app.module.ts b/node-cli/assets/nest/example-app-pg/src/app.module.ts new file mode 100644 index 00000000..6f383b64 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/app.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DatabaseModule } from '@database/database.module'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; +import { AuthModule } from '@api/auth/auth.module'; +import { UsersModule } from '@api/users/users.module'; +import { TodosModule } from '@api/todos/todos.module'; +import { nodeConfig, dbConfig, authConfig } from '@utils/environment'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + cache: true, + ignoreEnvFile: true, + isGlobal: true, + }), + DatabaseModule, + HealthchecksModule, + UsersModule, + AuthModule, + TodosModule, + ], +}) +export class AppModule {} diff --git a/node-cli/assets/nest/example-app-pg/src/database/base-repository.repository.spec.ts b/node-cli/assets/nest/example-app-pg/src/database/base-repository.repository.spec.ts new file mode 100644 index 00000000..c6431bad --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/base-repository.repository.spec.ts @@ -0,0 +1,76 @@ +import { Test } from '@nestjs/testing'; +import { NestKnexService } from '@database/nest-knex.service'; +import { BaseRepository } from './base-repository.repository'; +import { Knex } from 'knex'; +import { SortOrder } from '@utils/query'; + +describe('BaseRepository', () => { + let baseRepository: BaseRepository; + + const where = jest.fn((): Promise => Promise.resolve()); + const whereILike = jest.fn((): Promise => Promise.resolve()); + const orderBy = jest.fn((): Promise => Promise.resolve()); + const first = jest.fn((): Promise => Promise.resolve()); + + const count = jest.fn().mockImplementation(() => ({ + first, + })); + + const clone = jest.fn().mockImplementation(() => ({ + count, + })); + + const qb = { + where, + whereILike, + orderBy, + clone, + } as unknown as Knex.QueryBuilder; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: NestKnexService, + useFactory: () => ({ + repository: () => ({ + qb, + }), + }), + }, + BaseRepository, + ], + }).compile(); + + baseRepository = moduleRef.get>(BaseRepository); + }); + + it('where', async () => { + await baseRepository.where(qb, 'testName' as any, 'name'); + + expect(where).toHaveBeenCalled(); + expect(where).toHaveBeenCalledWith({ ['name']: 'testName' }); + }); + + it('whereLike', async () => { + await baseRepository.whereLike(qb, 'testName' as any, 'name'); + + expect(whereILike).toHaveBeenCalled(); + expect(whereILike).toHaveBeenCalledWith('name', `%testName%`); + }); + + it('orderBy', async () => { + await baseRepository.orderBy(qb, SortOrder.Asc, 'name'); + + expect(orderBy).toHaveBeenCalled(); + expect(orderBy).toHaveBeenCalledWith('name', SortOrder.Asc); + }); + + it('count', async () => { + first.mockResolvedValueOnce({ count: 1 }); + + const result = await baseRepository.count(qb); + + expect(result).toBe(1); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/database/base-repository.repository.ts b/node-cli/assets/nest/example-app-pg/src/database/base-repository.repository.ts new file mode 100644 index 00000000..e971062a --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/base-repository.repository.ts @@ -0,0 +1,38 @@ +import { NestKnexService } from '@database/nest-knex.service'; +import { SortOrder } from '@utils/query'; +import { Knex } from 'knex'; + +// eslint-disable-next-line @typescript-eslint/ban-types +export class BaseRepository { + private knexService; + + constructor(knex: NestKnexService, private readonly tableName: string) { + this.knexService = knex; + } + + repository() { + return this.knexService.connection(this.tableName); + } + + async count(qb: Knex.QueryBuilder): Promise { + const totalCount = await qb.clone().count().first(); + + return +totalCount.count; + } + + where(qb: Knex.QueryBuilder, value: Partial, column: string) { + return qb.where({ [column]: value }); + } + + whereLike( + qb: Knex.QueryBuilder, + value: Partial, + column: string, + ) { + return qb.whereILike(column, `%${value}%`); + } + + orderBy(qb: Knex.QueryBuilder, order: SortOrder, column: string) { + return qb.orderBy(column, order); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/database/constants.ts b/node-cli/assets/nest/example-app-pg/src/database/constants.ts new file mode 100644 index 00000000..cc806173 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/constants.ts @@ -0,0 +1,7 @@ +export const KNEX_CONNECTION = 'KNEX_CONNECTION'; +export const NEST_KNEX_OPTIONS = 'NEST_KNEX_OPTIONS'; + +export enum Tables { + Users = 'users', + Todos = 'todos', +} diff --git a/node-cli/assets/nest/example-app-pg/src/database/database.module.ts b/node-cli/assets/nest/example-app-pg/src/database/database.module.ts new file mode 100644 index 00000000..2f93d789 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/database.module.ts @@ -0,0 +1,54 @@ +import { Module, OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import * as pg from 'pg'; +import { dbConfig, nodeConfig } from '@utils/environment'; +import { KNEX_CONNECTION, NEST_KNEX_OPTIONS } from './constants'; +import { NestKnexService } from './nest-knex.service'; + +@Module({ + providers: [ + NestKnexService, + { + provide: NEST_KNEX_OPTIONS, + inject: [dbConfig.KEY, nodeConfig.KEY], + useFactory: ( + database: ConfigType, + node: ConfigType, + ) => ({ + client: 'pg', + useNullAsDefault: true, + connection: { + host: database.PGHOST, + port: database.PGPORT, + user: database.PGUSER, + password: database.PGPASSWORD, + database: database.PGDATABASE, + }, + seeds: { + directory: `./src/database/seeds/${node.NODE_ENV}`, + }, + }), + }, + { + provide: KNEX_CONNECTION, + useFactory: async (nestKnexService: NestKnexService) => + nestKnexService.connection, + inject: [NestKnexService], + }, + ], + exports: [NestKnexService], +}) +export class DatabaseModule implements OnModuleInit, OnApplicationShutdown { + constructor(private readonly knex: NestKnexService) {} + + onModuleInit() { + // https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js + pg.types.setTypeParser(pg.types.builtins.INT8, parseInt); + pg.types.setTypeParser(pg.types.builtins.NUMERIC, parseFloat); + pg.types.setTypeParser(pg.types.builtins.DATE, (v) => v); // keep as string for now + } + + async onApplicationShutdown(): Promise { + await this.knex.connection.destroy(); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts new file mode 100644 index 00000000..d71d7805 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.spec.ts @@ -0,0 +1,46 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { foreignKeyViolation } from './foreign-key-violation.error-mapping'; +import { RecordNotFoundError } from '@database/errors'; + +describe('foreignKeyViolation', () => { + it('should return an error mapping', () => { + expect(foreignKeyViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = foreignKeyViolation( + 'constraint-name', + 'error-message', + ).isError; + + it('should return true when the error is a foreign key constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.FOREIGN_KEY_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a foreign key constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = foreignKeyViolation( + 'constraint-name', + 'error-message', + ).newError; + + it('should return an instance of RecordNotFound with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(RecordNotFoundError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.ts b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.ts new file mode 100644 index 00000000..47d378b1 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/foreign-key-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { RecordNotFoundError } from '@database/errors'; +import { isForeignKeyViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const foreignKeyViolation = ( + constraint: string, + message: string, +): ErrorMapping => ({ + isError: isForeignKeyViolation(constraint), + newError: () => new RecordNotFoundError(message), +}); diff --git a/node-cli/assets/nest/example-app-pg/src/database/error-mappings/index.ts b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/index.ts new file mode 100644 index 00000000..9f743b83 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/index.ts @@ -0,0 +1,2 @@ +export * from './foreign-key-violation.error-mapping'; +export * from './unique-violation.error-mapping'; diff --git a/node-cli/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.spec.ts b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.spec.ts new file mode 100644 index 00000000..6ab85c7d --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.spec.ts @@ -0,0 +1,43 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; +import { uniqueViolation } from './unique-violation.error-mapping'; +import { DuplicateRecordError } from '@database/errors'; + +describe('uniqueViolation', () => { + it('should return an error mapping', () => { + expect(uniqueViolation('constraint-name', 'error-message')).toEqual({ + isError: expect.any(Function), + newError: expect.any(Function), + }); + }); + + describe('isError', () => { + const isError = uniqueViolation('constraint-name', 'error-message').isError; + + it('should return true when the error is a unique constraint violation error', () => { + const error = new DatabaseError('error', 1, 'error'); + error.code = PostgresError.UNIQUE_VIOLATION; + error.constraint = 'constraint-name'; + expect(isError(error)).toBe(true); + }); + + it('should return false when the error is not a unique constraint violation error', () => { + expect(isError(new Error('error'))).toBe(false); + expect(isError('error')).toBe(false); + expect(isError({ message: 'error' })).toBe(false); + }); + }); + + describe('newError', () => { + const newError = uniqueViolation( + 'constraint-name', + 'error-message', + ).newError; + + it('should return an instance of DuplicateRecord with the provided error message', () => { + const error = newError(); + expect(error).toBeInstanceOf(DuplicateRecordError); + expect(error.message).toBe('error-message'); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.ts b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.ts new file mode 100644 index 00000000..0aa2afbf --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/error-mappings/unique-violation.error-mapping.ts @@ -0,0 +1,11 @@ +import { DuplicateRecordError } from '@database/errors'; +import { isUniqueViolation } from '@database/utils/error'; +import { ErrorMapping } from '@utils/error'; + +export const uniqueViolation = ( + constraint: string, + message: string, +): ErrorMapping => ({ + isError: isUniqueViolation(constraint), + newError: () => new DuplicateRecordError(message), +}); diff --git a/node-cli/assets/nest/example-app-pg/src/database/errors/duplicate-record.error.ts b/node-cli/assets/nest/example-app-pg/src/database/errors/duplicate-record.error.ts new file mode 100644 index 00000000..65fbab07 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/errors/duplicate-record.error.ts @@ -0,0 +1,6 @@ +export class DuplicateRecordError extends Error { + constructor(message: string) { + super(message); + this.name = DuplicateRecordError.name; + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/database/errors/index.ts b/node-cli/assets/nest/example-app-pg/src/database/errors/index.ts new file mode 100644 index 00000000..7099d226 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/errors/index.ts @@ -0,0 +1,2 @@ +export * from './duplicate-record.error'; +export * from './record-not-found.error'; diff --git a/node-cli/assets/nest/example-app-pg/src/database/errors/record-not-found.error.ts b/node-cli/assets/nest/example-app-pg/src/database/errors/record-not-found.error.ts new file mode 100644 index 00000000..52bcb4fa --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/errors/record-not-found.error.ts @@ -0,0 +1,6 @@ +export class RecordNotFoundError extends Error { + constructor(message: string) { + super(message); + this.name = RecordNotFoundError.name; + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/database/extensions/knex/index.ts b/node-cli/assets/nest/example-app-pg/src/database/extensions/knex/index.ts new file mode 100644 index 00000000..825cda2d --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/extensions/knex/index.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +import Knex from 'knex'; +import { filter, sort, paginate } from '@database/query-builder'; +import { FilterMap, Pagination, Sort, SorterMap } from '@utils/query'; + +declare module 'knex' { + namespace Knex { + interface QueryBuilder { + /** + * Given a column-to-value map and column-to-filter map, applies the filters to the query builder. + * Filter map must cover all columns in the column-to-value map. + * + * Example: + * ``` + * queryBuilder.filter( + * { + * name: 'John', + * isAncient: true, + * }, + * { + * name: (queryBuilder, name) => queryBuilder.whereILike('name', `%${name}%`), + * isAncient: (queryBuilder, isAncient) => queryBuilder.where('age', isAncient ? '>=' : '<', 100), + * } + * ); + * ``` + */ + filter( + filters: Filters | undefined, + filterMap: FilterMap, + ): this; + /** + * Given a list of column sortings an a column-to-sorter map, applies the sorters to the query builder. + * Sorter map must cover all columns in the column sortings list. + * + * Example: + * ``` + * queryBuilder.sort( + * [ + * { column: 'name' }, + * { column: 'age', order: 'desc'} + * ], + * { + * name: (queryBuilder, order) => queryBuilder.orderBy('name', order), + * age: (queryBuilder, order) => queryBuilder.orderBy('age', order), + * } + * ); + * ``` + */ + sort( + sorts: Sort[] | undefined, + sorterMap: SorterMap, + ): this; + /** + * Offset pagination. This a shorthand for the following: + * + * ``` + * queryBuilder + * .offset((pagination.page - 1) * pagination.items) + * .limit(pagination.items); + * ``` + */ + paginate(pagination?: Pagination): this; + } + } +} + +export const register = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.filter) { + Knex.QueryBuilder.extend('filter', function (filters, filterMap) { + return filters ? filter(this, filters, filterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.sort) { + Knex.QueryBuilder.extend('sort', function (sorts, sorterMap) { + return sorts ? sort(this, sorts || [], sorterMap) : this; + }); + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!Knex.paginate) { + Knex.QueryBuilder.extend('paginate', function (pagination) { + return paginate(this, pagination); + }); + } +}; diff --git a/node-cli/assets/nest/example-app-pg/src/database/extensions/knex/register.ts b/node-cli/assets/nest/example-app-pg/src/database/extensions/knex/register.ts new file mode 100644 index 00000000..07230200 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/extensions/knex/register.ts @@ -0,0 +1,3 @@ +import { register } from './index'; + +register(); diff --git a/node-cli/assets/nest/example-app-pg/src/database/nest-knex.service.ts b/node-cli/assets/nest/example-app-pg/src/database/nest-knex.service.ts new file mode 100644 index 00000000..1b21b6ea --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/nest-knex.service.ts @@ -0,0 +1,14 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { NEST_KNEX_OPTIONS } from './constants'; +import { knex, Knex } from 'knex'; + +@Injectable() +export class NestKnexService { + public connection: Knex; + + constructor( + @Inject(NEST_KNEX_OPTIONS) private _NestKnexOptions: Knex.Config, + ) { + this.connection = knex(this._NestKnexOptions); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/database/query-builder/extensions.spec.ts b/node-cli/assets/nest/example-app-pg/src/database/query-builder/extensions.spec.ts new file mode 100644 index 00000000..a73f6db1 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/query-builder/extensions.spec.ts @@ -0,0 +1,94 @@ +import { Pagination, Sort, SortOrder } from '@utils/query'; +import { filter, sort, paginate } from './extensions'; + +describe('filter', () => { + it('should apply all the filters to the query builder', () => { + const queryBuilder = {}; + + const filters = { + name: 'John', + height: undefined, + age: 42, + }; + + const filterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _name: string) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _age: number) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + height: jest.fn((qb, _height: number) => qb), + }; + + filter(queryBuilder as never, filters, filterMap as never); + + expect(filterMap.name).toHaveBeenCalledWith( + queryBuilder, + filters.name, + Object.keys(filterMap)[0], + ); + expect(filterMap.height).not.toHaveBeenCalled(); + expect(filterMap.age).toHaveBeenCalledWith( + queryBuilder, + filters.age, + Object.keys(filterMap)[1], + ); + }); +}); + +describe('sort', () => { + it('should apply all the sortings to the query builder', () => { + const queryBuilder = {}; + + const sorts: Sort<'name' | 'age'>[] = [ + { column: 'name' }, + { column: 'age', order: SortOrder.Desc }, + ]; + + const sorterMap = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + name: jest.fn((qb, _order) => qb), + // eslint-disable-next-line @typescript-eslint/no-unused-vars + age: jest.fn((qb, _order) => qb), + }; + + sort(queryBuilder as never, sorts, sorterMap); + + expect(sorterMap.name).toHaveBeenCalledWith( + queryBuilder, + SortOrder.Asc, + Object.keys(sorterMap)[0], + ); + expect(sorterMap.age).toHaveBeenCalledWith( + queryBuilder, + SortOrder.Desc, + Object.keys(sorterMap)[1], + ); + }); +}); + +describe('paginate', () => { + it('should apply an offset and a limit to the query builder', () => { + const queryBuilder = { + offset() { + return this; + }, + limit() { + return this; + }, + }; + + jest.spyOn(queryBuilder, 'offset'); + jest.spyOn(queryBuilder, 'limit'); + + const pagination: Pagination = { + pageNumber: 3, + pageSize: 15, + }; + + paginate(queryBuilder as never, pagination); + + expect(queryBuilder.offset).toHaveBeenCalledWith(30); + expect(queryBuilder.limit).toHaveBeenCalledWith(15); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/database/query-builder/extensions.ts b/node-cli/assets/nest/example-app-pg/src/database/query-builder/extensions.ts new file mode 100644 index 00000000..00d47324 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/query-builder/extensions.ts @@ -0,0 +1,50 @@ +import { Knex } from 'knex'; +import { + Sort, + Pagination, + FilterMap, + SorterMap, + extractPagination, + SortOrder, +} from '@utils/query'; + +// +// Knex.QueryBuilder extensions +// +export const filter = < + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + Query extends Record, + Filters extends FilterMap>, +>( + qb: Knex.QueryBuilder, + filters: Query, + filterMap: Filters, +): Knex.QueryBuilder => + Object.entries(filters) + .filter(([, v]) => v !== undefined) + .reduce>( + (qb, [k, v]) => filterMap[k as keyof Filters](qb, v as never, k as never), + qb, + ); + +// eslint-disable-next-line @typescript-eslint/ban-types +export const sort = ( + qb: Knex.QueryBuilder, + sorts: Sort[], + sorterMap: SorterMap, +): Knex.QueryBuilder => + sorts.reduce>( + (qb, sort) => + sorterMap[sort.column](qb, sort.order || SortOrder.Asc, sort.column), + qb, + ); + +export const paginate = ( + qb: QB, + pagination?: Pagination, +) => { + const { pageNumber, pageSize } = extractPagination(pagination); + + return qb.offset((pageNumber - 1) * pageSize).limit(pageSize); +}; diff --git a/node-cli/assets/nest/example-app-pg/src/database/query-builder/index.ts b/node-cli/assets/nest/example-app-pg/src/database/query-builder/index.ts new file mode 100644 index 00000000..4509f5cc --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/query-builder/index.ts @@ -0,0 +1 @@ +export * from './extensions'; diff --git a/node-cli/assets/nest/example-app-pg/src/database/seeds/test/index.js b/node-cli/assets/nest/example-app-pg/src/database/seeds/test/index.js new file mode 100644 index 00000000..bab41cb5 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/seeds/test/index.js @@ -0,0 +1,37 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.seed = async function (knex) { + // Deletes ALL existing entries + await knex('users').del(); + await knex('users').insert([ + // The original password for this hash is 'pass@ord' + { + email: 'hello@email.com', + password: '$2b$10$Mxur7NOiTlm22yuldEMZgOCbIV7bxDCcUbBLFbzrJ1MrnIczZB.92', // pragma: allowlist secret + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); + + await knex('todos').insert([ + { + name: 'Laundry 1', + note: 'Buy detergent 1', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 2', + note: 'Buy detergent 2', + completed: false, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + { + name: 'Laundry 3', + note: 'Buy detergent 3', + completed: true, + userId: 'tz4a98xxat96iws9zmbrgj3a', + }, + ]); +}; diff --git a/node-cli/assets/nest/example-app-pg/src/database/utils/error.ts b/node-cli/assets/nest/example-app-pg/src/database/utils/error.ts new file mode 100644 index 00000000..38cf35ea --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/utils/error.ts @@ -0,0 +1,17 @@ +import { DatabaseError } from 'pg'; +import { PostgresError } from 'pg-error-enum'; + +export const isDatabaseError = (e: unknown): e is DatabaseError => + e instanceof DatabaseError && + e.code !== undefined && + e.constraint !== undefined; + +export const isUniqueViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.UNIQUE_VIOLATION && + e.constraint === constraint; + +export const isForeignKeyViolation = (constraint: string) => (e: unknown) => + isDatabaseError(e) && + e.code === PostgresError.FOREIGN_KEY_VIOLATION && + e.constraint === constraint; diff --git a/node-cli/assets/nest/example-app-pg/src/database/utils/index.ts b/node-cli/assets/nest/example-app-pg/src/database/utils/index.ts new file mode 100644 index 00000000..93ae819e --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/database/utils/index.ts @@ -0,0 +1 @@ +export * from './error'; diff --git a/node-cli/assets/nest/example-app-pg/src/main.ts b/node-cli/assets/nest/example-app-pg/src/main.ts new file mode 100644 index 00000000..248ec0b5 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/main.ts @@ -0,0 +1,66 @@ +import '@database/extensions/knex/register'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import helmet from '@fastify/helmet'; +import compression from '@fastify/compress'; +import { HttpAdapterHost, NestFactory } from '@nestjs/core'; +import { ConfigType } from '@nestjs/config'; +import { AppModule } from './app.module'; +import { ValidationPipe } from '@nestjs/common'; +import { RequestLoggingInterceptor } from '@utils/interceptors/request-logging.interceptor'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { ErrorLoggingFilter } from '@utils/error-logging.filter'; +import { nodeConfig } from '@utils/environment'; + +async function bootstrap() { + const app = await NestFactory.create( + AppModule, + new FastifyAdapter(), + ); + + // enables CORS + app.enableCors(); + + // add security HTTP headers + app.register(helmet); + + // compresses response bodies + app.register(compression); + + // enable validation globally + app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); + + // map application level errors to http errors + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + // setup graceful shutdown + app.enableShutdownHooks(); + + const configService = app.get>(nodeConfig.KEY); + const port = configService.PORT; + + if (configService.REQUEST_LOGGING) { + app.useGlobalInterceptors(new RequestLoggingInterceptor()); + } + + if (configService.SWAGGER) { + const config = new DocumentBuilder().setTitle('To-Do Example API').build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('/swagger', app, document); + } + + if (configService.ERROR_LOGGING) { + const httpAdapterHost = app.get(HttpAdapterHost); + app.useGlobalFilters(new ErrorLoggingFilter(httpAdapterHost)); + } + + // start server + await app.listen(port, '0.0.0.0', () => { + console.log(`App is running on http://localhost:${port}`); + }); +} + +bootstrap(); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/api/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/api/index.ts new file mode 100644 index 00000000..dbc1ea0f --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/api/index.ts @@ -0,0 +1 @@ +export * from './response'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/api/response.ts b/node-cli/assets/nest/example-app-pg/src/utils/api/response.ts new file mode 100644 index 00000000..89983b94 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/api/response.ts @@ -0,0 +1,16 @@ +import { Paginated, Pagination, extractPagination } from '@utils/query'; + +export const paginatedResponse = ( + items: T[], + total: number, + pagination?: Pagination, +): Paginated => { + const { pageNumber, pageSize } = extractPagination(pagination); + + return { + items, + total: total, + currentPage: pageNumber, + totalPages: Math.ceil(total / pageSize), + }; +}; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/index.ts new file mode 100644 index 00000000..fcada5a3 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/index.ts @@ -0,0 +1,2 @@ +export * from './lower-case'; +export * from './trim'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/lower-case.ts b/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/lower-case.ts new file mode 100644 index 00000000..03897a90 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/lower-case.ts @@ -0,0 +1,16 @@ +import { Transform } from 'class-transformer'; + +const toLower = (v: string) => v.toLowerCase(); + +export const LowerCase = () => + Transform( + (params) => + Array.isArray(params.value) + ? params.value.map(toLower) + : typeof params.value === 'string' + ? toLower(params.value) + : params.value, + { + toClassOnly: true, + }, + ); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/trim.ts b/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/trim.ts new file mode 100644 index 00000000..b261cbce --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/class-transformers/trim.ts @@ -0,0 +1,4 @@ +import { Transform } from 'class-transformer'; + +export const Trim = () => + Transform((params) => params.value?.trim(), { toClassOnly: true }); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/decorators/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/decorators/index.ts new file mode 100644 index 00000000..3f75d993 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/decorators/index.ts @@ -0,0 +1 @@ +export * from './public.decorator'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/decorators/public.decorator.spec.ts b/node-cli/assets/nest/example-app-pg/src/utils/decorators/public.decorator.spec.ts new file mode 100644 index 00000000..b4aab11b --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/decorators/public.decorator.spec.ts @@ -0,0 +1,20 @@ +import { IS_PUBLIC_KEY, Public } from './public.decorator'; + +describe('@Public', () => { + @Public() + class PublicRoutes {} + + class GuardedRoutes {} + + it('should be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, PublicRoutes); + + expect(isPublic).toBe(true); + }); + + it('should not be marked as public', () => { + const isPublic = Reflect.getMetadata(IS_PUBLIC_KEY, GuardedRoutes); + + expect(isPublic).toBe(undefined); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/decorators/public.decorator.ts b/node-cli/assets/nest/example-app-pg/src/utils/decorators/public.decorator.ts new file mode 100644 index 00000000..b3845e12 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/decorators/public.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/dtos/errors.dto.ts b/node-cli/assets/nest/example-app-pg/src/utils/dtos/errors.dto.ts new file mode 100644 index 00000000..661bfa22 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/dtos/errors.dto.ts @@ -0,0 +1,63 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Errors } from '@utils/enums'; + +interface ErrorDto { + statusCode: number; + message: string; + error: string; +} + +export class BadRequestDto implements ErrorDto { + @ApiProperty({ example: 400 }) + statusCode: number; + + @ApiProperty({ example: Errors.BadRequest }) + message: string; + + @ApiProperty({ example: Errors.BadRequest }) + error: string; +} + +export class UnauthorizedDto implements ErrorDto { + @ApiProperty({ example: 401 }) + statusCode: number; + + @ApiProperty({ example: Errors.Unauthorized }) + message: string; + + @ApiProperty({ example: Errors.Unauthorized }) + error: string; +} + +export class NotFoundDto implements ErrorDto { + @ApiProperty({ example: 404 }) + statusCode: number; + + @ApiProperty({ example: 'Record not found' }) + message: string; + + @ApiProperty({ example: Errors.NotFound }) + error: string; +} + +export class ConflictDto implements ErrorDto { + @ApiProperty({ example: 409 }) + statusCode: number; + + @ApiProperty({ example: 'Record already exists' }) + message: string; + + @ApiProperty({ example: Errors.Conflict }) + error: string; +} + +export class UnprocessableEntityDto implements ErrorDto { + @ApiProperty({ example: 422 }) + statusCode: number; + + @ApiProperty({ example: 'Invalid input' }) + message: string; + + @ApiProperty({ example: Errors.UnprocessableEntity }) + error: string; +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/dtos/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/dtos/index.ts new file mode 100644 index 00000000..1a520955 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/dtos/index.ts @@ -0,0 +1,2 @@ +export * from './pagination.dto'; +export * from './errors.dto'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/dtos/pagination.dto.ts b/node-cli/assets/nest/example-app-pg/src/utils/dtos/pagination.dto.ts new file mode 100644 index 00000000..0bdf43ae --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/dtos/pagination.dto.ts @@ -0,0 +1,14 @@ +import { Transform } from 'class-transformer'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class PaginationDto { + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageNumber?: number; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + pageSize?: number; +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/entities/generic-entity.ts b/node-cli/assets/nest/example-app-pg/src/utils/entities/generic-entity.ts new file mode 100644 index 00000000..301c5824 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/entities/generic-entity.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class GenericEntity { + @ApiProperty() + id: number; + + @ApiProperty() + createdAt: string; + + @ApiProperty() + updatedAt: string; +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/entities/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/entities/index.ts new file mode 100644 index 00000000..1fdd9774 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/entities/index.ts @@ -0,0 +1 @@ +export * from './generic-entity'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/enums/errors.enum.ts b/node-cli/assets/nest/example-app-pg/src/utils/enums/errors.enum.ts new file mode 100644 index 00000000..8d775147 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/enums/errors.enum.ts @@ -0,0 +1,7 @@ +export enum Errors { + BadRequest = 'Bad Request', + Unauthorized = 'Unauthorized', + NotFound = 'Not Found', + Conflict = 'Conflict', + UnprocessableEntity = 'Unprocessable Entity', +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/enums/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/enums/index.ts new file mode 100644 index 00000000..efe62e58 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/enums/index.ts @@ -0,0 +1 @@ +export * from './errors.enum'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/error-logging.filter.spec.ts b/node-cli/assets/nest/example-app-pg/src/utils/error-logging.filter.spec.ts new file mode 100644 index 00000000..8b6e8362 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/error-logging.filter.spec.ts @@ -0,0 +1,68 @@ +import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core'; +import { + ArgumentsHost, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { ErrorLoggingFilter } from './error-logging.filter'; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +jest.spyOn(Logger.prototype, 'error').mockImplementation(() => {}); + +describe('Error logging filter', () => { + let httpAdapterHost: HttpAdapterHost; + let errorLoggingFilter: ErrorLoggingFilter; + const mockStatus = jest.fn(); + const mockGetResponse = jest.fn().mockImplementation(() => ({ + status: mockStatus, + })); + const mockHttpArgumentsHost = jest.fn().mockImplementation(() => ({ + getResponse: mockGetResponse, + })); + const mockArgumentsHost = { + switchToHttp: mockHttpArgumentsHost, + getArgByIndex: jest.fn(), + getArgs: jest.fn(), + getType: jest.fn(), + switchToRpc: jest.fn(), + switchToWs: jest.fn(), + }; + beforeEach(() => { + httpAdapterHost = new HttpAdapterHost(); + httpAdapterHost.httpAdapter = { + reply: jest.fn(), + } as any; + errorLoggingFilter = new ErrorLoggingFilter(httpAdapterHost); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('returns status 500 and logs the error when it is not a generic HTTP exception', () => { + const err = new Error('Generic'); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith(err); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + err, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + }); + + it('returns the status of the HTTP exception and logs the stringified error', () => { + const err = new HttpException('Exception', HttpStatus.BAD_REQUEST); + errorLoggingFilter.catch(err, mockArgumentsHost); + + expect(errorLoggingFilter.logger.error).toHaveBeenCalledWith('"Exception"'); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledTimes(1); + expect(httpAdapterHost.httpAdapter.reply).toHaveBeenCalledWith( + { status: mockStatus }, + 'Exception', + HttpStatus.BAD_REQUEST, + ); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/error-logging.filter.ts b/node-cli/assets/nest/example-app-pg/src/utils/error-logging.filter.ts new file mode 100644 index 00000000..3371caf5 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/error-logging.filter.ts @@ -0,0 +1,37 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { HttpAdapterHost } from '@nestjs/core'; + +@Catch() +export class ErrorLoggingFilter implements ExceptionFilter { + public logger = new Logger('ErrorLoggingFilter'); + + constructor(private readonly httpAdapterHost: HttpAdapterHost) {} + + public catch(exception: unknown, host: ArgumentsHost): void { + const { httpAdapter } = this.httpAdapterHost; + const ctx = host.switchToHttp(); + + const httpStatus = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + if (exception instanceof HttpException) { + const error = exception.getResponse(); + this.logger.error(JSON.stringify(error)); + + return httpAdapter.reply(ctx.getResponse(), error, httpStatus); + } + + this.logger.error(exception); + + return httpAdapter.reply(ctx.getResponse(), exception, httpStatus); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/error.spec.ts b/node-cli/assets/nest/example-app-pg/src/utils/error.spec.ts new file mode 100644 index 00000000..a0b8baa7 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/error.spec.ts @@ -0,0 +1,59 @@ +import { ErrorMapping, mapError, rethrowError } from './error'; + +describe('mapError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const newError = new TypeError('type-error'); + + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return newError; + }, + }; + + it('should return the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(mapError(mapping)(error)).toBe(error); + }); + + it('should return the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(mapError(mapping)(error)).toBe(newError); + }); + }); +}); + +describe('rethrowError', () => { + it('should return a function', () => { + expect(mapError()).toBeInstanceOf(Function); + }); + + describe('the returned function', () => { + const mapping: ErrorMapping = { + isError(e) { + return e instanceof Error && e.message === 'some-message'; + }, + newError() { + return new TypeError('type-error'); + }, + }; + + it('should rethrow the original error when no mapping matched it', () => { + const error = new Error('message'); + expect(() => rethrowError(mapping)(error)).toThrowError(error); + }); + + it('should rethrow the matched error when a mapping matched it', () => { + const error = new Error('some-message'); + expect(() => rethrowError(mapping)(error)).toThrowError( + new TypeError('type-error'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/error.ts b/node-cli/assets/nest/example-app-pg/src/utils/error.ts new file mode 100644 index 00000000..df376a09 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/error.ts @@ -0,0 +1,28 @@ +type IsErrorPredicate = (error: unknown) => boolean; + +type ErrorFactory = () => Error; + +export interface ErrorMapping { + isError: IsErrorPredicate; + newError: ErrorFactory; +} + +export const mapError = function (...mappings: ErrorMapping[]) { + return function (err: Error) { + for (const { isError, newError } of mappings) { + if (isError(err)) { + return newError(); + } + } + + return err; + }; +}; + +export const rethrowError = function (...mappings: ErrorMapping[]) { + const errorMapper = mapError(...mappings); + + return function (err: Error) { + throw errorMapper(err); + }; +}; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/interceptors/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/interceptors/index.ts new file mode 100644 index 00000000..9841f297 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/interceptors/index.ts @@ -0,0 +1 @@ +export * from './service-to-http-error.interceptor'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/interceptors/request-logging.interceptor.ts b/node-cli/assets/nest/example-app-pg/src/utils/interceptors/request-logging.interceptor.ts new file mode 100644 index 00000000..b8d9197b --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/interceptors/request-logging.interceptor.ts @@ -0,0 +1,83 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, + Logger, +} from '@nestjs/common'; +import { catchError, Observable, tap, throwError } from 'rxjs'; + +const REMOVED = '[[REMOVED]]'; + +@Injectable() +export class RequestLoggingInterceptor implements NestInterceptor { + private logger = new Logger('RequestLoggingInterceptor'); + + private sanitizeHeaders(headers: Record): Record { + const sanitizedHeaders = { ...headers }; + + if (sanitizedHeaders.Authorization) { + sanitizedHeaders.Authorization = REMOVED; + } + + if (sanitizedHeaders.Cookie) { + sanitizedHeaders.Cookie = REMOVED; + } + + return sanitizedHeaders; + } + + private sanitizeBody(body: Record): Record { + const sanitizedBody = { ...body }; + + if (sanitizedBody.password) { + sanitizedBody.password = REMOVED; + } + + return sanitizedBody; + } + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const timestamp = new Date().toISOString(); + const startTime = process.hrtime(); + + const { url, body, method, headers, ip } = context + .switchToHttp() + .getRequest(); + + return next.handle().pipe( + tap(() => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + }; + + this.logger.log(JSON.stringify(logMsg)); + }), + catchError((err: any) => { + const endTime = process.hrtime(startTime); + const duration = endTime[0] * 1000 + endTime[1] / 1000000; + const logMsg = { + timestamp, + duration: `${duration}ms`, + ip, + headers: this.sanitizeHeaders(headers), + method, + url, + body: this.sanitizeBody(body), + err, + }; + + this.logger.log(JSON.stringify(logMsg)); + return throwError(() => err); + }), + ); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/interceptors/service-to-http-error.interceptor.ts b/node-cli/assets/nest/example-app-pg/src/utils/interceptors/service-to-http-error.interceptor.ts new file mode 100644 index 00000000..3a4313fc --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/interceptors/service-to-http-error.interceptor.ts @@ -0,0 +1,31 @@ +import { + CallHandler, + ConflictException, + ExecutionContext, + Injectable, + NestInterceptor, + NotFoundException, +} from '@nestjs/common'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { DuplicateRecordError, RecordNotFoundError } from '@database/errors'; + +const map = { + [RecordNotFoundError.name]: NotFoundException, + [DuplicateRecordError.name]: ConflictException, +}; + +@Injectable() +export class ServiceToHttpErrorsInterceptor implements NestInterceptor { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + catchError((err: Error) => + throwError(() => { + const klass = map[err.name]; + return klass ? new klass(err.message) : err; + }), + ), + ); + } +} diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/error.spec.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/error.spec.ts new file mode 100644 index 00000000..7f02759d --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/error.spec.ts @@ -0,0 +1,47 @@ +import { RecordNotFoundError } from '@database/errors'; +import { definedOrNotFound, updatedOrNotFound } from './error'; + +describe('definedOrNotFound', () => { + it('should return a function', () => { + expect(typeof definedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the value it is called with when the value is not undefined', () => { + expect(definedOrNotFound('message')(null)).toBe(null); + expect(definedOrNotFound('message')(false)).toBe(false); + expect(definedOrNotFound('message')(0)).toBe(0); + expect(definedOrNotFound('message')('')).toBe(''); + const emptyArray: unknown[] = []; + expect(definedOrNotFound('message')(emptyArray)).toBe(emptyArray); + const emptyObject = {}; + expect(definedOrNotFound('message')(emptyObject)).toEqual(emptyObject); + }); + + it('should throw RecordNotFound when the value it is called with is undefined', () => { + expect(() => definedOrNotFound('message')(undefined)).toThrowError( + new RecordNotFoundError('message'), + ); + }); + }); +}); + +describe('updatedOrNotFound', () => { + it('should return a function', () => { + expect(typeof updatedOrNotFound('message')).toBe('function'); + }); + + describe('returned function', () => { + it('should return the number it is called with when the number is not zero', () => { + expect(updatedOrNotFound('message')(-1)).toBe(-1); + expect(updatedOrNotFound('message')(1)).toBe(1); + expect(updatedOrNotFound('message')(NaN)).toEqual(NaN); + }); + + it('should throw RecordNotFound when the value it is called with is zero', () => { + expect(() => updatedOrNotFound('message')(0)).toThrowError( + new RecordNotFoundError('message'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/error.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/error.ts new file mode 100644 index 00000000..b903287e --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/error.ts @@ -0,0 +1,25 @@ +import { RecordNotFoundError } from '@database/errors'; + +const definedOrThrow = (errorFactory: () => Error) => { + return (result: T | undefined): T => { + if (result === undefined) { + throw errorFactory(); + } + return result; + }; +}; + +const updatedOrThrow = (errorFactory: () => Error) => { + return (result: number): number => { + if (result === 0) { + throw errorFactory(); + } + return result; + }; +}; + +export const definedOrNotFound = (message: string) => + definedOrThrow(() => new RecordNotFoundError(message)); + +export const updatedOrNotFound = (message: string) => + updatedOrThrow(() => new RecordNotFoundError(message)); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/filtration.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/filtration.ts new file mode 100644 index 00000000..b5f151eb --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/filtration.ts @@ -0,0 +1,52 @@ +import { Knex } from 'knex'; + +/** + * A Filter is a function that accepts a QueryBuilder for a given entity and a value, + * applies the filter to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type FilterByName = Filter, Todo['name']>; + * + * const filterByName: FilterByName = (qb, name) => qb.where({ name }); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Filter = ( + qb: Knex.QueryBuilder, + value: Value, + key: Key, +) => Knex.QueryBuilder; + +/** + * A FilerMap is a map is a mapping from query param names to filters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all query parameters have a corresponding filter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * // This would typically be obtained via z.infer + * interface ListTodosFilters { + * id?: number; + * name?: string; + * }; + * + * const listTodosFilterMap: FilterMap, ListTodosFilters> = { + * id: (qb, id) => qb.where({ id }), + * name: (qb, name) => qb.whereILike('name', `%${name}%`), + * }; + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type FilterMap = { + // Since query params are usually optional, + // we loop through the query params removing their optionality via `-?`, + // then map them to a filter of their value excluding undefined. + [K in keyof Filters]-?: Filter, undefined>, K>; +}; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/index.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/index.ts new file mode 100644 index 00000000..3bad38e7 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/index.ts @@ -0,0 +1,4 @@ +export * from './error'; +export * from './filtration'; +export * from './pagination'; +export * from './sorting'; diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/pagination.spec.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/pagination.spec.ts new file mode 100644 index 00000000..378c4a4d --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/pagination.spec.ts @@ -0,0 +1,24 @@ +import { + Pagination, + extractPagination, + paginationDefaults, +} from './pagination'; + +describe('extractPagination', () => { + it('should return the provided pagination', () => { + const pagination: Pagination = { pageNumber: 1, pageSize: 10 }; + expect(extractPagination(pagination)).toEqual(pagination); + }); + + it('should supply the defaults for any missing pagination properties', () => { + expect(extractPagination()).toEqual(paginationDefaults); + expect(extractPagination({ pageNumber: 1 })).toEqual({ + ...paginationDefaults, + pageNumber: 1, + }); + expect(extractPagination({ pageSize: 10 })).toEqual({ + ...paginationDefaults, + pageSize: 10, + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/pagination.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/pagination.ts new file mode 100644 index 00000000..a5e5d6e8 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/pagination.ts @@ -0,0 +1,24 @@ +export const paginationDefaults = { + pageNumber: 1, + pageSize: 20, +}; + +export interface Pagination { + pageNumber?: number; + pageSize?: number; +} + +interface PaginationResponse { + totalPages: number; + currentPage: number; +} + +export interface Paginated extends PaginationResponse { + items: Entity[]; + total: number; +} + +export const extractPagination = (pagination?: Pagination) => ({ + ...paginationDefaults, + ...pagination, +}); diff --git a/node-cli/assets/nest/example-app-pg/src/utils/query/sorting.ts b/node-cli/assets/nest/example-app-pg/src/utils/query/sorting.ts new file mode 100644 index 00000000..5c9554ef --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/src/utils/query/sorting.ts @@ -0,0 +1,56 @@ +import { Knex } from 'knex'; + +export enum SortOrder { + Asc = 'asc', + Desc = 'desc', +} + +export interface Sort { + column: SortColumn; + order?: SortOrder; +} + +/** + * A Sorter is a function that accepts a QueryBuilder for a given entity and a sort order, + * applies the sorting to the QueryBuilder and returns it. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type SortByName = Sorter>; + * + * const sortByName: SortByName = (qb, order) => qb.orderBy('name', order); + * ``` + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export type Sorter = ( + qb: Knex.QueryBuilder, + order: SortOrder, + column: SortColumn, +) => Knex.QueryBuilder; + +/** + * A SorterMap is a map is a mapping from query param names to sorters. + * The type requires that the list of keys is exhaustive, in order to + * enforce that all sort parameters have a corresponding sorter implemented. + * + * Example: + * ``` + * import { Knex } from 'knex'; + * import { Tables } from 'knex/types/tables'; + * + * type ListTodoSortColumn = 'name' | 'createdAt'; + * + * const listTodosSorterMap: SorterMap, ListTodoSortColumn> = { + * name: (qb, order) => qb.orderBy('name', order), + * createdAt: (qb, order) => qb.orderBy('createdAt', order), + * }; + * ``` + */ +export type SorterMap< + // eslint-disable-next-line @typescript-eslint/ban-types + Model extends {}, + SortColumn extends string, +> = Record>; diff --git a/node-cli/assets/nest/example-app-pg/test/healthchecks.e2e-spec.ts b/node-cli/assets/nest/example-app-pg/test/healthchecks.e2e-spec.ts new file mode 100644 index 00000000..ed67bfa2 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/test/healthchecks.e2e-spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { HealthchecksModule } from '@api/healthchecks/healthchecks.module'; + +describe('GET /healthz', () => { + let app: NestFastifyApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [HealthchecksModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + + it('/live', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/live', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); + + it('/ready', async () => { + await app + .inject({ + method: 'GET', + url: '/healthz/ready', + }) + .then(({ statusCode, body }) => { + expect(statusCode).toBe(200); + expect(body).toBe('OK'); + }); + }); +}); diff --git a/node-cli/assets/nest/example-app-pg/test/jest-e2e.config.js b/node-cli/assets/nest/example-app-pg/test/jest-e2e.config.js new file mode 100644 index 00000000..2c2968fb --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/test/jest-e2e.config.js @@ -0,0 +1,42 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '..', + testEnvironment: 'node', + testRegex: '.e2e-spec.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + coverageDirectory: 'coverage-e2e', + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/.*spec.ts$', + '/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*repository.ts$', + '/src/.*guard.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, +}; diff --git a/node-cli/assets/nest/example-app-pg/test/utils/expect-error.ts b/node-cli/assets/nest/example-app-pg/test/utils/expect-error.ts new file mode 100644 index 00000000..b56c3b27 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/test/utils/expect-error.ts @@ -0,0 +1,11 @@ +import { HttpException } from '@nestjs/common'; + +export const expectError = async ( + ex: T, + jsonResponse: Response['json'], +) => { + const response = await jsonResponse(); + + expect(ex.message).toEqual(response.error || response.message); + expect(ex.getStatus()).toEqual(response.statusCode); +}; diff --git a/node-cli/assets/nest/example-app-pg/test/utils/index.ts b/node-cli/assets/nest/example-app-pg/test/utils/index.ts new file mode 100644 index 00000000..f788710c --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/test/utils/index.ts @@ -0,0 +1 @@ +export * from './expect-error'; diff --git a/node-cli/assets/nest/example-app-pg/tsconfig.build.json b/node-cli/assets/nest/example-app-pg/tsconfig.build.json new file mode 100644 index 00000000..f54d2265 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + "test", + "dist", + "**/*spec.ts", + "**/__mocks__" + ] +} diff --git a/node-cli/assets/nest/example-app-pg/tsconfig.json b/node-cli/assets/nest/example-app-pg/tsconfig.json new file mode 100644 index 00000000..d8131811 --- /dev/null +++ b/node-cli/assets/nest/example-app-pg/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "paths": { + "@api/*": ["src/api/*"], + "@database": ["src/database"], + "@database/*": ["src/database/*"], + "@extensions/*": ["src/extensions/*"], + "@middleware": ["src/middleware"], + "@utils/*": ["src/utils/*"] + } + }, + "ts-node": { + "require": ["tsconfig-paths/register", "dotenv/config"] + } +} diff --git a/node-cli/assets/nest/example-app/.devcontainer/devcontainer.json b/node-cli/assets/nest/example-app/.devcontainer/devcontainer.json new file mode 100644 index 00000000..3ff38d20 --- /dev/null +++ b/node-cli/assets/nest/example-app/.devcontainer/devcontainer.json @@ -0,0 +1,9 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Nest API DevContainer", + "dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"], + "service": "api", + "workspaceFolder": "/usr/src/app", + "remoteUser": "root" +} diff --git a/node-cli/assets/nest/example-app/.devcontainer/docker-compose.yml b/node-cli/assets/nest/example-app/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..b01c619a --- /dev/null +++ b/node-cli/assets/nest/example-app/.devcontainer/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + api: + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" diff --git a/node-cli/assets/nest/example-app/Dockerfile b/node-cli/assets/nest/example-app/Dockerfile new file mode 100644 index 00000000..759bf918 --- /dev/null +++ b/node-cli/assets/nest/example-app/Dockerfile @@ -0,0 +1,89 @@ +# +# 🧑‍💻 Development +# +FROM node:18-alpine AS dev + +# Update OS packages +RUN apk update && apk upgrade --no-cache + +# Set the working directory +WORKDIR /usr/src/app + +# Set to dev environment +ENV NODE_ENV development + +# Copy the source code +COPY --chown=node:node . . + +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # rebuild bcrypt + && npm rebuild bcrypt + +# +# 🏡 Production Build +# +FROM node:18-alpine as build + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# In order to run `npm run build` we need access to the Nest CLI. +# Nest CLI is a dev dependency. +COPY --chown=node:node --from=dev /usr/src/app/node_modules ./node_modules +# Copy source code +COPY --chown=node:node . . + +# Generate the production build. The build script runs "nest build" to compile the application. +RUN npm run build + +# Run clean install +RUN npm ci \ + # skip funding messages + --fund false \ + # skip pre/post scripts + --ignore-scripts \ + # skip vulnerability check + --no-audit \ + # skip update information + --no-update-notifier \ + # skip dev dependencies + --omit=dev \ + # rebuild bcrypt + && npm rebuild bcrypt \ + # attempt to reduce image size + && npm cache clean --force + +# Set Docker as a non-root user +USER node + +# +# 🚀 Production Server +# +FROM node:18-alpine as prod + +WORKDIR /usr/src/app +RUN apk update && apk upgrade --no-cache + +# Set to production environment +ENV NODE_ENV production + +# Copy only the necessary files +COPY --chown=node:node --from=build /usr/src/app/dist dist +COPY --chown=node:node --from=build /usr/src/app/node_modules node_modules + +# Set Docker as non-root user +USER node + +# Run +CMD [ "node", "./dist/src/main.js" ] \ No newline at end of file diff --git a/node-cli/assets/nest/example-app/jest.setup.ts b/node-cli/assets/nest/example-app/jest.setup.ts new file mode 100644 index 00000000..fe68937d --- /dev/null +++ b/node-cli/assets/nest/example-app/jest.setup.ts @@ -0,0 +1,4 @@ +import * as dotenv from 'dotenv'; +import * as path from 'path'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/assets/nest/link-script.sh b/node-cli/assets/nest/link-script.sh similarity index 100% rename from assets/nest/link-script.sh rename to node-cli/assets/nest/link-script.sh diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.controller.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.controller.ts new file mode 100644 index 00000000..dcb2db2e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.controller.ts @@ -0,0 +1,20 @@ +import { Body, Controller, Inject, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { CredentialsDto } from './dto'; +import { AuthService } from './services/auth.service'; +import { Public } from '@utils/decorators'; + +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor( + @Inject(AuthService) + private readonly authService: AuthService + ) {} + + @Public() + @Post('register') + register(@Body() credentials: CredentialsDto) { + return this.authService.register(credentials); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.module.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.module.ts new file mode 100644 index 00000000..0a25199c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/auth.module.ts @@ -0,0 +1,32 @@ +import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; +import { APP_GUARD } from '@nestjs/core'; +import { AuthGuard } from './guards'; +import { HttpModule } from '@nestjs/axios'; +import { AuthController } from './auth.controller'; +import { UsersModule } from '@api/users/users.module'; +import { ConfigModule } from '@nestjs/config'; +import { Auth0Service, AuthService, JwtStrategy } from './services'; + +export const AuthModuleMetadata = { + controllers: [AuthController], + imports: [ + HttpModule, + PassportModule.register({ defaultStrategy: 'jwt' }), + UsersModule, + ConfigModule, + ], + providers: [ + JwtStrategy, + { + provide: APP_GUARD, + useExisting: AuthGuard, + }, + AuthGuard, + Auth0Service, + AuthService, + ], +}; + +@Module(AuthModuleMetadata) +export class AuthModule {} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/credentials.dto.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/credentials.dto.ts new file mode 100644 index 00000000..bcfb161e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/credentials.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsEmail, + IsNotEmpty, + IsString, + MaxLength, + MinLength, +} from 'class-validator'; +import { LowerCase, Trim } from '@utils/class-transformers'; + +export class CredentialsDto { + @ApiProperty({ example: 'john@mail.com' }) + @Trim() + @LowerCase() + @IsEmail() + @MaxLength(255) + email: string; + + @ApiProperty({ example: 'MyS3cr37Pass' }) + @Trim() + @IsString() + @IsNotEmpty() + @MinLength(6) + @MaxLength(255) + password: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/index.ts new file mode 100644 index 00000000..9a2cf87c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/dto/index.ts @@ -0,0 +1 @@ +export * from './credentials.dto'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.spec.ts new file mode 100644 index 00000000..bb5eaf6e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.spec.ts @@ -0,0 +1,44 @@ +import { createMock } from '@golevelup/ts-jest'; +import { ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Test } from '@nestjs/testing'; +import { AuthGuard } from './auth.guard'; + +describe('Auth Guard', () => { + let authGuard: AuthGuard; + let reflector: Reflector; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [AuthGuard, Reflector], + }).compile(); + + authGuard = moduleRef.get(AuthGuard); + reflector = moduleRef.get(Reflector); + }); + + it('should be defined', () => { + expect(authGuard).toBeDefined(); + }); + + describe('canActivate', () => { + it('should return true if route is public', () => { + reflector.getAllAndOverride = jest.fn().mockReturnValue(true); + const mockContext = createMock(); + expect(authGuard.canActivate(mockContext)).toBe(true); + }); + + it("should call the super's object `canActivate` method", () => { + reflector.getAllAndOverride = jest.fn().mockReturnValue(false); + const superObj = Object.getPrototypeOf(Object.getPrototypeOf(authGuard)); + superObj.canActivate = jest.fn().mockReturnValue(true); + + const mockContext = createMock(); + const result = authGuard.canActivate(mockContext); + + expect(superObj.canActivate).toHaveBeenCalledTimes(1); + expect(superObj.canActivate).toHaveBeenCalledWith(mockContext); + expect(result).toBe(true); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.ts new file mode 100644 index 00000000..0fd9bbc1 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/auth.guard.ts @@ -0,0 +1,27 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_KEY } from '@utils/decorators/public.decorator'; +import { AuthGuard as PassportAuthGuard } from '@nestjs/passport'; + +@Injectable() +export class AuthGuard extends PassportAuthGuard('jwt') { + constructor(private reflector: Reflector) { + super(); + } + + private isPublic(context: ExecutionContext): boolean { + return this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + } + + canActivate(context: ExecutionContext) { + // Early return if the route is public + if (this.isPublic(context)) { + return true; + } + + return super.canActivate(context); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/index.ts new file mode 100644 index 00000000..b41e34a4 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/guards/index.ts @@ -0,0 +1 @@ +export * from './auth.guard'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/auth.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/auth.ts new file mode 100644 index 00000000..2e607b80 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/auth.ts @@ -0,0 +1,32 @@ +export interface JwtClaims { + sub: string; + email: string; +} + +export interface UserData { + user: JwtClaims; +} + +export interface Credentials { + email: string; + password: string; +} + +export type Auth0User = { + blocked: boolean; + created_at: string; + email: string; + email_verified: boolean; + identities: { + connection: string; + user_id: string; + provider: string; + isSocial: boolean; + }[]; + name: string; + nickname: string; + picture: string; + updated_at: string; + user_id: string; + user_metadata: Record; +}; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/interfaces/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.spec.ts new file mode 100644 index 00000000..b4f6956b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.spec.ts @@ -0,0 +1,124 @@ +import { User } from '@api/users/entities'; +import { UsersRepository } from '@api/users/repositories'; +import { AuthService } from './auth.service'; +import { BadRequestException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModuleMetadata } from '../auth.module'; +import { Auth0User, Credentials } from '../interfaces'; +import { Auth0Service } from './auth0.service'; +import { ObjectId } from 'mongodb'; +import { DatabaseService } from '@database/database.service'; +import { NEST_MONGO_OPTIONS } from '@database/constants'; +import { HttpModule } from '@nestjs/axios'; +import { PassportModule } from '@nestjs/passport'; +import { ConfigModule } from '@nestjs/config'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +const userCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const registeredUser: User = { + _id: new ObjectId(1), + createdAt: Date.now(), + updatedAt: Date.now(), + email: userCreds.email, + userId: '1', +}; + +const auth0User: Auth0User = { + blocked: false, + created_at: Date.now().toString(), + email: userCreds.email, + email_verified: true, + identities: [], + name: 'userName', + nickname: 'userNickname', + picture: '', + updated_at: Date.now().toString(), + user_id: '1', + user_metadata: {}, +}; + +describe('AuthService', () => { + let authService: AuthService; + let auth0Service: Auth0Service; + let usersRepository: UsersRepository; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [...AuthModuleMetadata.controllers], + imports: [ + HttpModule, + PassportModule.register({ defaultStrategy: 'jwt' }), + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + providers: [ + ...AuthModuleMetadata.providers, + { + provide: NEST_MONGO_OPTIONS, + useValue: { + urlString: 'mongodb://mock-host', + databaseName: 'test', + clientOptions: {}, + migrationsDir: '../../migrations', + seedsDir: './seeds/test', + }, + }, + DatabaseService, + UsersRepository, + ], + }).compile(); + + authService = module.get(AuthService); + auth0Service = module.get(Auth0Service); + usersRepository = module.get(UsersRepository); + + authService.logger.warn = jest.fn(); + authService.logger.error = jest.fn(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('register', () => { + it('when the email is not registered should register the user', async () => { + const createdUser = { ...registeredUser, ...userCreds }; + jest.spyOn(auth0Service, 'createUser').mockResolvedValueOnce(auth0User); + jest + .spyOn(usersRepository, 'insertOne') + .mockResolvedValueOnce(createdUser._id); + jest + .spyOn(auth0Service, 'updateUserMetadata') + .mockResolvedValueOnce(auth0User as any); + + const userId = await authService.register(userCreds); + + expect(userId).toEqual(createdUser._id); + }); + + it('throws error when creating a user in the database fails', async () => { + jest.spyOn(auth0Service, 'createUser').mockResolvedValueOnce(auth0User); + jest.spyOn(usersRepository, 'insertOne').mockRejectedValueOnce(undefined); + jest + .spyOn(auth0Service, 'deleteUser') + .mockResolvedValueOnce(auth0User as any); + + await expect(authService.register(userCreds)).rejects.toThrow( + new BadRequestException('Something went wrong!'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.ts new file mode 100644 index 00000000..9eaf39cf --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth.service.ts @@ -0,0 +1,40 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { CredentialsDto } from '../dto'; +import { UsersRepository } from '@api/users/repositories'; +import { Auth0Service } from './auth0.service'; + +@Injectable() +export class AuthService { + public logger = new Logger('AuthService'); + + constructor( + private usersRepository: UsersRepository, + private auth0Service: Auth0Service, + ) {} + + async register({ email, password }: CredentialsDto) { + const userAuth0 = await this.auth0Service.createUser(email, password); + + const userMongObjectId = await this.usersRepository + .insertOne({ + email, + userId: userAuth0.user_id, + }) + .catch(async (error) => { + this.logger.warn( + 'Creating a user in the database failed. Proceeding with deleting it in Auth0.', + ); + this.logger.error(error); + + await this.auth0Service.deleteUser(userAuth0.user_id); + + throw new BadRequestException('Something went wrong!'); + }); + + await this.auth0Service.updateUserMetadata(userAuth0.user_id, { + id: userMongObjectId, + }); + + return userMongObjectId; + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.spec.ts new file mode 100644 index 00000000..67988d34 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.spec.ts @@ -0,0 +1,222 @@ +import { BadRequestException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModuleMetadata } from '../auth.module'; +import { Auth0User, Credentials } from '../interfaces'; +import { Auth0Service } from './auth0.service'; +import { HttpModule, HttpService } from '@nestjs/axios'; +import { PassportModule } from '@nestjs/passport'; +import { ConfigModule } from '@nestjs/config'; +import { DatabaseService } from '@database/database.service'; +import { UsersRepository } from '@api/users/repositories'; +import { NEST_MONGO_OPTIONS } from '@database/constants'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +const userCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const auth0User: Auth0User = { + blocked: false, + created_at: Date.now().toString(), + email: userCreds.email, + email_verified: true, + identities: [], + name: 'userName', + nickname: 'userNickname', + picture: '', + updated_at: Date.now().toString(), + user_id: '1', + user_metadata: {}, +}; + +describe('Auth0Service', () => { + let httpService: HttpService; + let auth0Service: Auth0Service; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [...AuthModuleMetadata.controllers], + imports: [ + HttpModule, + PassportModule.register({ defaultStrategy: 'jwt' }), + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + providers: [ + ...AuthModuleMetadata.providers, + { + provide: NEST_MONGO_OPTIONS, + useValue: { + urlString: 'mongodb://mock-host', + databaseName: 'test', + clientOptions: {}, + migrationsDir: '../../migrations', + seedsDir: './seeds/test', + }, + }, + DatabaseService, + UsersRepository, + ], + }).compile(); + + auth0Service = module.get(Auth0Service); + httpService = module.get(HttpService); + + auth0Service.logger.warn = jest.fn(); + auth0Service.logger.error = jest.fn(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('createUser', () => { + it('when the email is not registered should register the user', async () => { + jest + .spyOn(auth0Service, 'searchUsersByEmail') + .mockResolvedValueOnce([] as unknown as Auth0User); + jest + .spyOn(httpService.axiosRef, 'post') + .mockResolvedValueOnce({ data: auth0User }); + + const user = await auth0Service.createUser( + userCreds.email, + userCreds.password, + ); + + expect(user).toEqual(auth0User); + }); + + it('throws error when user is already registered', async () => { + jest + .spyOn(auth0Service, 'searchUsersByEmail') + .mockResolvedValueOnce([auth0User] as unknown as Auth0User); + + await expect( + auth0Service.createUser(userCreds.email, userCreds.password), + ).rejects.toThrow( + new BadRequestException('A user with this email already exists.'), + ); + }); + + it('throws error when auth0 request fails', async () => { + jest + .spyOn(auth0Service, 'searchUsersByEmail') + .mockResolvedValueOnce([] as unknown as Auth0User); + jest + .spyOn(httpService.axiosRef, 'post') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect( + auth0Service.createUser(userCreds.email, userCreds.password), + ).rejects.toThrow(new BadRequestException()); + }); + }); + + it('updateUserMetadata updates metadata', async () => { + const newMetadata = { data: 'new' }; + jest + .spyOn(httpService.axiosRef, 'patch') + .mockResolvedValueOnce({ ...auth0User, user_metadata: newMetadata }); + + const user = await auth0Service.updateUserMetadata('1', newMetadata); + + expect(user).toEqual({ ...auth0User, user_metadata: newMetadata }); + }); + + describe('searchUsersByEmail', () => { + it('when the user is found', async () => { + jest + .spyOn(httpService.axiosRef, 'get') + .mockResolvedValueOnce({ data: auth0User }); + + const user = await auth0Service.searchUsersByEmail(auth0User.email); + + expect(user).toEqual(auth0User); + }); + + it('throws error when user is not found', async () => { + jest + .spyOn(httpService.axiosRef, 'get') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect( + auth0Service.searchUsersByEmail(auth0User.email), + ).rejects.toThrow(new BadRequestException()); + }); + }); + + describe('searchUsersByEmail', () => { + it('when user is deleted', async () => { + jest.spyOn(httpService.axiosRef, 'delete').mockResolvedValueOnce({}); + + const response = await auth0Service.deleteUser(auth0User.user_id); + + expect(response).toEqual({}); + }); + + it('throws error when auth0 request fails', async () => { + jest + .spyOn(httpService.axiosRef, 'delete') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect(auth0Service.deleteUser(auth0User.user_id)).rejects.toThrow( + new BadRequestException(), + ); + }); + }); + + describe('onModuleInit', () => { + it('when accessToken is set', async () => { + const tokenResponse = { data: { access_token: 'token' } }; + jest + .spyOn(httpService.axiosRef, 'post') + .mockResolvedValueOnce(tokenResponse); + + const onModuleInit = jest.spyOn(auth0Service, 'onModuleInit'); + + auth0Service.onModuleInit(); + + expect(onModuleInit).toHaveBeenCalled(); + }); + + it('throws error accessToken is missing', async () => { + const tokenResponse = { data: {} }; + jest + .spyOn(httpService.axiosRef, 'post') + .mockResolvedValueOnce(tokenResponse); + + await expect(auth0Service.onModuleInit()).rejects.toThrow( + new Error('Access token is missing!'), + ); + }); + + it('throws error when axios response is falsy(undefined)', async () => { + jest.spyOn(httpService.axiosRef, 'post').mockResolvedValueOnce(undefined); + + await expect(auth0Service.onModuleInit()).rejects.toThrowError( + new Error('Access token is missing!'), + ); + }); + + it('throws error when auth0 token request fails', async () => { + jest + .spyOn(httpService.axiosRef, 'post') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect(auth0Service.onModuleInit()).rejects.toThrow( + new Error('Something went wrong!'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.ts new file mode 100644 index 00000000..4b7a1612 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/auth0.service.ts @@ -0,0 +1,136 @@ +import { HttpService } from '@nestjs/axios'; +import { + BadRequestException, + Inject, + Injectable, + Logger, + OnModuleInit, +} from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import { authConfig } from '@utils/environment'; +import { Auth0User } from '../interfaces'; + +@Injectable() +export class Auth0Service implements OnModuleInit { + public logger = new Logger('Auth0Service'); + + private accessToken = ''; + private baseURL = this.auth.AUTH0_ISSUER_URL; + private auth0ClientId = this.auth.AUTH0_CLIENT_ID; + private auth0ClientSecret = this.auth.AUTH0_CLIENT_SECRET; + + constructor( + private httpService: HttpService, + @Inject(authConfig.KEY) + private auth: ConfigType, + ) {} + + onModuleInit() { + return this.getAuth0AccessToken() + .then((token) => { + this.accessToken = token; + }) + .catch((err) => { + throw err; + }); + } + + private async getAuth0AccessToken() { + const response = await this.httpService.axiosRef + .post<{ access_token: string }>( + `${this.baseURL}oauth/token`, + new URLSearchParams({ + grant_type: 'client_credentials', + client_id: this.auth0ClientId, + client_secret: this.auth0ClientSecret, + audience: `${this.baseURL}api/v2/`, + }), + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + }, + ) + .catch((error) => { + this.logger.error(error.response.data); + throw new Error('Something went wrong!'); + }); + + if (!response?.data?.access_token) { + this.logger.error('Access token is missing in the response data'); + throw new Error('Access token is missing!'); + } + + return response.data.access_token; + } + + private buildHeaders() { + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: `Bearer ${this.accessToken}`, + }; + } + + public async createUser(email: string, password: string) { + const users = await this.searchUsersByEmail(email); + if (Array.isArray(users) && users.length) { + throw new BadRequestException('A user with this email already exists.'); + } + + return this.httpService.axiosRef + .post( + `${this.baseURL}api/v2/users`, + { + email, + user_metadata: {}, + connection: 'Username-Password-Authentication', + password, + verify_email: true, + }, + { headers: this.buildHeaders() }, + ) + .then(({ data }) => data) + .catch((error) => { + this.logger.error(error.response.data); + throw new BadRequestException(); + }); + } + + public updateUserMetadata( + userId: string, + metadata: Auth0User['user_metadata'], + ) { + return this.httpService.axiosRef.patch( + `${this.baseURL}api/v2/users/${userId}`, + { + user_metadata: metadata, + }, + { headers: this.buildHeaders() }, + ); + } + + public searchUsersByEmail(email: string) { + return this.httpService.axiosRef + .get(`${this.baseURL}api/v2/users-by-email`, { + params: { email }, + headers: this.buildHeaders(), + }) + .then(({ data }) => data) + .catch((error) => { + this.logger.error(error.response.data); + throw new BadRequestException(); + }); + } + + public deleteUser(userId: string) { + return this.httpService.axiosRef + .delete(`${this.baseURL}api/v2/users/${userId}`, { + headers: this.buildHeaders(), + }) + .catch((error) => { + this.logger.error(error.response.data); + throw new BadRequestException(); + }); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/index.ts new file mode 100644 index 00000000..6a299d20 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/index.ts @@ -0,0 +1,3 @@ +export * from './auth.service'; +export * from './jwt.strategy'; +export * from './auth0.service'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.spec.ts new file mode 100644 index 00000000..7412cdd8 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.spec.ts @@ -0,0 +1,81 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModuleMetadata } from '../auth.module'; +import { Auth0User, Credentials } from '../interfaces'; +import { ConfigModule } from '@nestjs/config'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; +import { JwtStrategy } from './jwt.strategy'; +import { NEST_MONGO_OPTIONS } from '@database/constants'; +import { DatabaseService } from '@database/database.service'; +import { UsersRepository } from '@api/users/repositories'; +import { HttpModule } from '@nestjs/axios'; + +const userCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const auth0User: Auth0User = { + blocked: false, + created_at: Date.now().toString(), + email: userCreds.email, + email_verified: true, + identities: [], + name: 'userName', + nickname: 'userNickname', + picture: '', + updated_at: Date.now().toString(), + user_id: '1', + user_metadata: {}, +}; + +describe('JWT Strategy', () => { + let jwtStrategy: JwtStrategy; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + HttpModule, + ConfigModule.forRoot({ + load: [authConfig, dbConfig, nodeConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + providers: [ + ...AuthModuleMetadata.providers, + { + provide: NEST_MONGO_OPTIONS, + useValue: { + urlString: 'mongodb://mock-host', + databaseName: 'test', + clientOptions: {}, + migrationsDir: '../../migrations', + seedsDir: './seeds/test', + }, + }, + DatabaseService, + UsersRepository, + ], + }).compile(); + + jwtStrategy = module.get(JwtStrategy); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('validate', () => { + it('should return the Auth0User', () => { + const result = jwtStrategy.validate(auth0User); + + expect(result).toEqual(auth0User); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.ts new file mode 100644 index 00000000..5f6c9c0c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/auth0/services/jwt.strategy.ts @@ -0,0 +1,33 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { passportJwtSecret } from 'jwks-rsa'; +import { Auth0User } from '../interfaces'; +import { authConfig } from '@utils/environment'; +import { ConfigType } from '@nestjs/config'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor( + @Inject(authConfig.KEY) + config: ConfigType, + ) { + super({ + secretOrKeyProvider: passportJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `${config.AUTH0_ISSUER_URL}.well-known/jwks.json`, + }), + + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + audience: config.AUTH0_AUDIENCE, + issuer: `${config.AUTH0_ISSUER_URL}`, + algorithms: ['RS256'], + }); + } + + validate(payload: Auth0User): Auth0User { + return payload; + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.controller.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.controller.ts new file mode 100644 index 00000000..e562d91f --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.controller.ts @@ -0,0 +1,67 @@ +import { Body, Controller, HttpCode, Inject, Post } from '@nestjs/common'; +import { + ApiBody, + ApiConflictResponse, + ApiOkResponse, + ApiOperation, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { CredentialsDto } from './dto'; +import { AuthService } from './services'; +import { Public } from '@utils/decorators'; +import { JwtToken } from './entities'; +import { Errors } from '@utils/enums'; +import { ConflictDto, UnprocessableEntityDto } from '@utils/dtos'; + +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor( + @Inject(AuthService) + private readonly authService: AuthService + ) {} + + @Post('register') + @Public() + @HttpCode(200) + @ApiOperation({ + summary: 'Register a user', + description: 'Register a user', + }) + @ApiBody({ type: CredentialsDto }) + @ApiOkResponse({ + description: 'OK', + type: JwtToken, + }) + @ApiConflictResponse({ + description: Errors.Conflict, + type: ConflictDto, + }) + @ApiUnprocessableEntityResponse({ + description: Errors.UnprocessableEntity, + type: UnprocessableEntityDto, + }) + register(@Body() credentials: CredentialsDto): Promise { + return this.authService.register(credentials); + } + + @Post('login') + @Public() + @ApiOperation({ + summary: 'Login a user', + description: 'Authenticate a user', + }) + @ApiBody({ type: CredentialsDto }) + @ApiOkResponse({ + description: 'OK', + type: JwtToken, + }) + @ApiUnprocessableEntityResponse({ + description: Errors.UnprocessableEntity, + type: UnprocessableEntityDto, + }) + async login(@Body() credentials: CredentialsDto): Promise { + return this.authService.login(credentials); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.module.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.module.ts new file mode 100644 index 00000000..bb469a3b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/auth.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './auth.controller'; +import { AuthService, JwtService, PasswordService } from './services'; +import { ConfigModule } from '@nestjs/config'; +import { UsersModule } from '@api/users/users.module'; +import { APP_GUARD } from '@nestjs/core'; +import { AuthGuard } from './guards'; + +export const AuthModuleMetadata = { + imports: [ConfigModule, UsersModule], + providers: [ + JwtService, + PasswordService, + AuthService, + { + provide: APP_GUARD, + useExisting: AuthGuard, + }, + AuthGuard, + ], + controllers: [AuthController], +}; + +@Module(AuthModuleMetadata) +export class AuthModule {} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/credentials.dto.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/credentials.dto.ts new file mode 100644 index 00000000..bcfb161e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/credentials.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsEmail, + IsNotEmpty, + IsString, + MaxLength, + MinLength, +} from 'class-validator'; +import { LowerCase, Trim } from '@utils/class-transformers'; + +export class CredentialsDto { + @ApiProperty({ example: 'john@mail.com' }) + @Trim() + @LowerCase() + @IsEmail() + @MaxLength(255) + email: string; + + @ApiProperty({ example: 'MyS3cr37Pass' }) + @Trim() + @IsString() + @IsNotEmpty() + @MinLength(6) + @MaxLength(255) + password: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/index.ts new file mode 100644 index 00000000..9a2cf87c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/dto/index.ts @@ -0,0 +1 @@ +export * from './credentials.dto'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/index.ts new file mode 100644 index 00000000..799fdad6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/index.ts @@ -0,0 +1 @@ +export * from './jwt-token.entity'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/jwt-token.entity.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/jwt-token.entity.ts new file mode 100644 index 00000000..dad4d0b9 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/entities/jwt-token.entity.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class JwtToken { + @ApiProperty() + idToken: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.spec.ts new file mode 100644 index 00000000..2235743d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.spec.ts @@ -0,0 +1,96 @@ +import { createMock } from '@golevelup/ts-jest'; +import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { Reflector } from '@nestjs/core'; +import jwt from 'jsonwebtoken'; +import { Test } from '@nestjs/testing'; +import { AuthGuard } from './auth.guard'; +import { JwtClaims } from '../interfaces'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +describe('Auth Guard', () => { + let authGuard: AuthGuard; + let reflector: Reflector; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + ignoreEnvFile: true, + isGlobal: true, + }), + ], + providers: [ConfigService, AuthGuard, Reflector], + }).compile(); + + authGuard = moduleRef.get(AuthGuard); + reflector = moduleRef.get(Reflector); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + it('should be defined', () => { + expect(authGuard).toBeDefined(); + }); + + describe('canActivate', () => { + const testClaims: JwtClaims = { + sub: 'tz4a98xxat96iws9zmbrgj3a', + email: 'admin@admin.com', + }; + + it('should return true if route is public', () => { + reflector.getAllAndOverride = jest.fn().mockReturnValue(true); + const mockContext = createMock(); + expect(authGuard.canActivate(mockContext)).toBe(true); + }); + + it('should return true when user is authenticated', () => { + const mockContext = createMock(); + mockContext.switchToHttp().getRequest.mockReturnValue({ + headers: { + authorization: 'Bearer testToken', + }, + }); + jest.spyOn(jwt, 'verify').mockReturnValue(testClaims as never); + + expect(authGuard.canActivate(mockContext)).toBe(true); + }); + + it('should throw an Unauthorized error when user is not authenticated', () => { + const mockContext = createMock(); + mockContext.switchToHttp().getRequest.mockReturnValue({ + headers: { + authorization: 'Bearer invalidToken', + }, + }); + jest.spyOn(jwt, 'verify').mockImplementation(() => { + throw new Error(); + }); + + expect(() => authGuard.canActivate(mockContext)).toThrow( + UnauthorizedException, + ); + }); + + it('should throw an Unauthorized error when token is not send', () => { + const mockContext = createMock(); + mockContext.switchToHttp().getRequest.mockReturnValue({ + headers: {}, + }); + + expect(() => authGuard.canActivate(mockContext)).toThrow( + UnauthorizedException, + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.ts new file mode 100644 index 00000000..4d509e82 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/auth.guard.ts @@ -0,0 +1,59 @@ +import { + CanActivate, + ExecutionContext, + Inject, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_KEY } from '@utils/decorators/public.decorator'; +import { Request } from 'express-jwt'; +import { verify } from 'jsonwebtoken'; +import { JwtClaims } from '../interfaces'; +import { authConfig } from '@utils/environment'; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor( + @Inject(authConfig.KEY) private auth: ConfigType, + private reflector: Reflector, + ) {} + + canActivate(context: ExecutionContext): boolean { + // Early return if the route is public + if (this.isPublic(context)) { + return true; + } + + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + if (!token) { + throw new UnauthorizedException(); + } + + try { + const { sub, email } = verify( + token, + this.auth.JWT_SECRET, + ) as Partial; + request.user = { sub, email } as JwtClaims; + } catch { + throw new UnauthorizedException(); + } + + return true; + } + + private extractTokenFromHeader(request: Request): string | undefined { + const [type, token] = request.headers.authorization?.split(' ') ?? []; + return type === 'Bearer' ? token : undefined; + } + + private isPublic(context: ExecutionContext): boolean { + return this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/index.ts new file mode 100644 index 00000000..b41e34a4 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/guards/index.ts @@ -0,0 +1 @@ +export * from './auth.guard'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/auth.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/auth.ts new file mode 100644 index 00000000..a3054dac --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/auth.ts @@ -0,0 +1,15 @@ +import { ObjectId } from 'mongodb'; + +export interface JwtClaims { + sub: string; + email: string; +} + +export interface UserData { + user: JwtClaims; +} + +export interface Credentials { + email: string; + password: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/interfaces/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.spec.ts new file mode 100644 index 00000000..a7a19226 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.spec.ts @@ -0,0 +1,161 @@ +import { User } from '@api/users/entities'; +import { UsersRepository } from '@api/users/repositories'; +import { JwtService } from './jwt.service'; +import { PasswordService } from './password.service'; +import { AuthService } from './auth.service'; +import { + ConflictException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Credentials } from '../interfaces'; +import { ObjectId } from 'mongodb'; +import { ConfigModule } from '@nestjs/config'; +import { APP_GUARD } from '@nestjs/core'; +import { AuthGuard } from '../guards'; +import { AuthController } from '../auth.controller'; +import { NEST_MONGO_OPTIONS } from '@database/constants'; +import { DatabaseService } from '@database/database.service'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +const registeredUser: User = { + _id: new ObjectId(100), + userId: new ObjectId(100), + createdAt: Date.now(), + updatedAt: Date.now(), + email: 'registered-email@example.com', + password: 'very-secret', +}; + +const unregisteredCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const registeredCreds: Credentials = { + email: 'registered@email.com', + password: 'very-secret', +}; + +describe('AuthService', () => { + let authService: AuthService; + let jwtService: JwtService; + let passwordService: PasswordService; + let usersRepository: UsersRepository; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + ignoreEnvFile: true, + isGlobal: true, + }), + ], + providers: [ + JwtService, + PasswordService, + AuthService, + { + provide: APP_GUARD, + useExisting: AuthGuard, + }, + AuthGuard, + { + provide: NEST_MONGO_OPTIONS, + useValue: { + urlString: 'mongodb://mock-host', + databaseName: 'test', + clientOptions: {}, + migrationsDir: '../../migrations', + seedsDir: './seeds/test', + }, + }, + DatabaseService, + UsersRepository, + ], + controllers: [AuthController], + }).compile(); + + authService = module.get(AuthService); + jwtService = module.get(JwtService); + passwordService = module.get(PasswordService); + usersRepository = module.get(UsersRepository); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('register', () => { + it('when the email is not registered should register the user', async () => { + jest.spyOn(usersRepository, 'findByEmail').mockResolvedValueOnce(null); + jest + .spyOn(usersRepository, 'insertOne') + .mockResolvedValueOnce(registeredUser._id); + jest + .spyOn(usersRepository, 'updateOne') + .mockResolvedValueOnce({ ...registeredUser, ...unregisteredCreds }); + jest.spyOn(jwtService, 'sign').mockReturnValueOnce('jwtTokenValue'); + + const response = await authService.register(unregisteredCreds); + + expect(response.idToken).toBe('jwtTokenValue'); + }); + + it('when the email is already registered should throw an error', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(registeredUser); + + await expect( + authService.register({ + email: registeredUser.email, + password: registeredUser.password!, + }), + ).rejects.toThrowError(new ConflictException('User email already taken')); + }); + }); + + describe('login', () => { + it('when the email is not registered should throw an error', async () => { + jest.spyOn(usersRepository, 'findByEmail').mockResolvedValueOnce(null); + + await expect(authService.login(unregisteredCreds)).rejects.toThrowError( + new UnprocessableEntityException('Invalid email or password'), + ); + }); + + it('when the email is registered and the password does not match should return an error', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(registeredUser); + + jest.spyOn(passwordService, 'compare').mockResolvedValueOnce(false); + + await expect(authService.login(registeredCreds)).rejects.toThrowError( + new UnprocessableEntityException('Invalid email or password'), + ); + }); + + it('when the email is registered and the email and password are valid should log the user', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(registeredUser); + + jest.spyOn(passwordService, 'compare').mockResolvedValueOnce(true); + + jest.spyOn(jwtService, 'sign').mockReturnValueOnce('jwtTokenValue'); + + const response = await authService.login(registeredCreds); + + expect(response.idToken).toBe('jwtTokenValue'); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.ts new file mode 100644 index 00000000..c2d87f96 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/auth.service.ts @@ -0,0 +1,62 @@ +import { JwtToken } from '../entities'; +import { + ConflictException, + Inject, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; +import { UsersRepository } from '@api/users/repositories'; +import { JwtService } from './jwt.service'; +import { PasswordService } from './password.service'; +import { Credentials } from '../interfaces'; + +@Injectable() +export class AuthService { + constructor( + @Inject(UsersRepository) + private readonly users: UsersRepository, + @Inject(JwtService) + private readonly jwt: JwtService, + @Inject(PasswordService) + private readonly password: PasswordService + ) {} + + async register({ email, password }: Credentials): Promise { + const existingUser = await this.users.findByEmail(email); + if (existingUser) { + throw new ConflictException('User email already taken'); + } + + const userId = await this.users.insertOne({ + email, + password: await this.password.hash(password), + }); + + return { + idToken: this.jwt.sign({ sub: userId.toString(), email }), + }; + } + + async login({ email, password }: Credentials): Promise { + const user = await this.users.findByEmail(email); + + if (!user) { + throw new UnprocessableEntityException('Invalid email or password'); + } + + const passwordMatches = await this.password.compare( + password, + user.password! + ); + + if (!passwordMatches) { + throw new UnprocessableEntityException('Invalid email or password'); + } + + const token = this.jwt.sign({ sub: user._id.toString(), email }); + + return { + idToken: token, + }; + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/index.ts new file mode 100644 index 00000000..a3c1b63b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/index.ts @@ -0,0 +1,3 @@ +export * from './auth.service'; +export * from './jwt.service'; +export * from './password.service'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.spec.ts new file mode 100644 index 00000000..943c4192 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.spec.ts @@ -0,0 +1,63 @@ +import { verify } from 'jsonwebtoken'; +import { JwtService } from './jwt.service'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { Test } from '@nestjs/testing'; +import { ObjectId } from 'mongodb'; +import { nodeConfig, dbConfig, authConfig } from '@utils/environment'; + +describe('JwtService', () => { + const env: { [key: string]: string } = { + JWT_SECRET: 'very-secret', + JWT_EXPIRATION: '1d', + }; + + let jwtService: JwtService; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + ignoreEnvFile: true, + isGlobal: true, + }), + ], + providers: [ + { + provide: authConfig.KEY, + useValue: env, + }, + JwtService, + ], + }).compile(); + + jwtService = moduleRef.get(JwtService); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('sign', () => { + it('should sign the claims returning a JWT token', () => { + const claims = { + sub: new ObjectId(1).toString(), + email: 'email@example.com', + }; + const token = jwtService.sign(claims); + const payload = verify(token, env.JWT_SECRET); + expect(payload).toEqual( + expect.objectContaining({ + email: claims.email, + sub: claims.sub.toString(), + }), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.ts new file mode 100644 index 00000000..1df0822c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/jwt.service.ts @@ -0,0 +1,18 @@ +import { sign } from 'jsonwebtoken'; +import { ConfigType } from '@nestjs/config'; +import { Inject, Injectable } from '@nestjs/common'; +import { JwtClaims } from '../interfaces'; +import { authConfig } from '@utils/environment'; + +@Injectable() +export class JwtService { + constructor( + @Inject(authConfig.KEY) private auth: ConfigType, + ) {} + + sign(claims: JwtClaims): string { + return sign(claims, this.auth.JWT_SECRET, { + expiresIn: this.auth.JWT_EXPIRATION, + }); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.spec.ts new file mode 100644 index 00000000..6f6c1f26 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.spec.ts @@ -0,0 +1,38 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PasswordService } from './password.service'; + +describe('PasswordService', () => { + let passwordsService: PasswordService; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + providers: [PasswordService], + }).compile(); + + passwordsService = app.get(PasswordService); + }); + + describe('hash', () => { + it('should return a different string', async () => { + expect(await passwordsService.hash('some-text')).not.toBe('some-text'); + }); + }); + + describe('compare', () => { + it('should be true when the hash is of the value', async () => { + const password = 'very-secret'; + const hash = await passwordsService.hash(password); + + expect(await passwordsService.compare(password, hash)).toBe(true); + }); + + it('should be false when the hash is not of the value', async () => { + const password = 'very-secret'; + const hash = await passwordsService.hash(password); + + expect(await passwordsService.compare('not-my-password', hash)).toBe( + false, + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.ts new file mode 100644 index 00000000..7003fc54 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/jwt/services/password.service.ts @@ -0,0 +1,13 @@ +import { hash, compare, genSalt } from 'bcrypt'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class PasswordService { + async hash(password: string): Promise { + return hash(password, await genSalt()); + } + + async compare(password: string, hash: string): Promise { + return compare(password, hash); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/jest-e2e.config.js b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/jest-e2e.config.js new file mode 100644 index 00000000..21834cad --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/jest-e2e.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '..', + testEnvironment: 'node', + testRegex: '.e2e-spec.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + coverageDirectory: 'coverage-e2e', + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/.*spec.ts$', + '/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*repository.ts$', + '/src/.*guard.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + '^@test/(.*)$': '/test/$1', + }, +}; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/create.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/create.e2e-spec.ts new file mode 100644 index 00000000..d40cc3aa --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/create.e2e-spec.ts @@ -0,0 +1,173 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + BadRequestException, + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { expectError, MongoDBTestSetup } from '@test/utils'; +import { Auth0Service } from '@api/auth/services'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; + +describe('POST /v1/todos', () => { + const todoPayload = getTodoPayload(); + const userId = new ObjectId(); + + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }) + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the created todo', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then(async (res) => { + const todo = await databaseService.connection + .collection('todos') + .findOne({ name: todoPayload.name }); + expect(res.statusCode).toEqual(201); + expect(res.payload).toEqual(`"${todo?._id}"`); + expect(todo?.note).toEqual(todoPayload.note); + expect(todo?.completed).toEqual(todoPayload.completed); + }); + }); + + it('should throw error if a todo with the same name already exists', async () => { + const { name, note, completed } = getTodoPayload(false); + await app.inject({ + method: 'POST', + url: '/v1/todos', + payload: { name, note, completed }, + }); + + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: { name, note, completed }, + }) + .then((res) => res.json()) + .then((response) => { + expect(response).toEqual({ + statusCode: 422, + message: 'Unprocessable Entity', + error: 'Unprocessable Entity', + }); + }); + }); + + it('should return 422 error when empty payload is provided', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: {}, + }) + .then((res) => expectError(new BadRequestException(), res.json)); + }); +}); + +describe('POST /v1/todos - real AuthGuard', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error when user is not authenticated', () => { + return app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-all.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-all.e2e-spec.ts new file mode 100644 index 00000000..dff3a71e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-all.e2e-spec.ts @@ -0,0 +1,323 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { sortByField } from './utils/sortby-field-todos'; +import { SortOrder } from '@utils/query'; +import { + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { expectError, MongoDBTestSetup } from '@test/utils'; +import { Auth0Service } from '@api/auth/services'; +import { DatabaseService } from '@database/database.service'; + +describe('GET /v1/todos', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + }) + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('when no filters are applied - should return whole todos list', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(3); + }); + }); + + it('should return completed todo list - when completed=true filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'true', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when completed=false filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'false', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return todo list with matched results - when name = "Laundry" filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos?filters[name]=Laundry', + query: { + name: 'Laundry 1', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '3', + pageNumber: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(0); + expect(total).toEqual(3); + }); + }); + + it('should return desc ordered todo list - when sort by name desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Desc) + ); + }); + + it('should return asc ordered todo list - when sort by name asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Asc) + ); + }); + + it('should return desc ordered todo list - when sort by createdAt desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Desc) + ); + }); + + it('should return asc ordered todo list - when sort by createdAt asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Asc) + ); + }); + + it('should return filtered todo list - when all filters are applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + pageNumber: '1', + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(2); + expect(total).toEqual(3); + }); + }); +}); + +describe('GET /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-one.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-one.e2e-spec.ts new file mode 100644 index 00000000..1e0626d7 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/find-one.e2e-spec.ts @@ -0,0 +1,152 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError, MongoDBTestSetup } from '@test/utils'; +import { Auth0Service } from '@api/auth/services'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; + +describe('GET /v1/todos/:id', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }) + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the todo - given todo id in the query', async () => { + const todo = ( + await databaseService.connection.collection('todos').find().toArray() + )[0]; + + await app + .inject({ + method: 'GET', + url: `/v1/todos/${todo._id}`, + }) + .then((res) => { + const { id, name, note, completed } = res.json(); + expect(res.statusCode).toBe(200); + + expect(id).toEqual(todo.id); + expect(name).toEqual(todo.name); + expect(note).toEqual(todo.note); + expect(completed).toEqual(todo.completed); + }); + }); + + it('should return 404', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'GET', + url: `/v1/todos/${objId}`, + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('GET /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'GET', + url: `/v1/todos/${objId}`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/remove.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/remove.e2e-spec.ts new file mode 100644 index 00000000..0da11e94 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/remove.e2e-spec.ts @@ -0,0 +1,147 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError, MongoDBTestSetup } from '@test/utils'; +import { Auth0Service } from '@api/auth/services'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; + +describe('DELETE /v1/todos', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }) + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 200 when todo is deleted', async () => { + const todos = await databaseService.connection + .collection('todos') + .find() + .toArray(); + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/${todos[0]._id}`, + }) + .then(async ({ statusCode }) => { + expect(statusCode).toBe(200); + const todo = await databaseService.connection + .collection('todos') + .findOne({ _id: todos[0]._id }); + expect(todo).toBeNull(); + }); + }); + + it('should return 404 - non existing todo', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/${objId}`, + }) + .then((res) => expectError(new NotFoundException(), res.json)); + }); +}); + +describe('DELETE /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/${objId}`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/update.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/update.e2e-spec.ts new file mode 100644 index 00000000..e44a60f5 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/update.e2e-spec.ts @@ -0,0 +1,186 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError, MongoDBTestSetup } from '@test/utils'; +import { Auth0Service } from '@api/auth/services'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId, WithId } from 'mongodb'; +import { Todo } from '@api/todos/entities'; + +describe('PUT /v1/todos/:id', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + let todo: WithId; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + + todo = ( + await databaseService.connection + .collection('todos') + .find() + .toArray() + )[0]; + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the updated todo', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${todo._id}`, + payload: { + name: 'updated', + completed: true, + note: 'updated', + }, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.name).toEqual('updated'); + expect(responseBody.note).toEqual('updated'); + expect(responseBody.completed).toEqual(true); + }); + }); + + it('should return the not updated todo', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${todo._id}`, + payload: {}, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.name).toEqual('Laundry 1'); + expect(responseBody.note).toEqual('Buy detergent 1'); + expect(responseBody.completed).toEqual(false); + }); + }); + + it('should return 404', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${objId}`, + payload: getTodoPayload(), + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('PUT /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let todo: WithId; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + todo = ( + await databaseService.connection + .collection('todos') + .find() + .toArray() + )[0]; + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${todo._id}`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/get-todo-payload.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/get-todo-payload.ts new file mode 100644 index 00000000..4147ce84 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/get-todo-payload.ts @@ -0,0 +1,9 @@ +import { CreateTodoDto } from '@api/todos/dto/create-todo.dto'; + +export const getTodoPayload: (completed?: boolean) => CreateTodoDto = ( + completed +) => ({ + name: 'Laundry' + Date.now(), + note: 'Buy detergent' + Date.now(), + completed: completed ?? false, +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/index.ts new file mode 100644 index 00000000..82299d0e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/index.ts @@ -0,0 +1,2 @@ +export * from './get-todo-payload'; +export * from './sortby-field-todos'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/sortby-field-todos.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/sortby-field-todos.ts new file mode 100644 index 00000000..e6eb92c6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/auth0/todos/utils/sortby-field-todos.ts @@ -0,0 +1,26 @@ +import { Todo } from '@api/todos/entities/todo.entity'; +import { SortOrder } from '@utils/query'; + +type ListTodoSortColumn = 'name' | 'createdAt'; + +export const sortByField: ( + list: Todo[], + field: ListTodoSortColumn, + order?: SortOrder +) => Todo[] = (list, field, order) => { + return list.sort((a, b) => { + if (order === 'desc') { + if (field === 'name') { + return b[field].localeCompare(a[field]); + } else { + return b[field] - a[field]; + } + } + + if (field === 'name') { + return a[field].localeCompare(b[field]); + } else { + return a[field] - b[field]; + } + }); +}; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/login.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/login.e2e-spec.ts new file mode 100644 index 00000000..410b9b16 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/login.e2e-spec.ts @@ -0,0 +1,113 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { expectError } from '../utils/expect-error'; +import { DatabaseService } from '@database/database.service'; +import { + BadRequestException, + UnprocessableEntityException, + ValidationPipe, +} from '@nestjs/common'; +import { MongoDBTestSetup } from '@test/utils'; + +describe('POST /auth/login', () => { + const credentials = { email: 'hello@email.com', password: 'pass@ord' }; + + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + await app.init(); + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('given the email and password are valid', () => { + it('should login the user and return a jwt token', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/login', + payload: credentials, + }) + .then((res) => { + expect(res.statusCode).toBe(201); + expect(typeof res.json().idToken).toBe('string'); + }); + }); + + describe('when there are empty credentials', () => { + it('should return 400', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/login', + }) + .then((res) => { + expect(expectError(new BadRequestException(), res.json)); + }); + }); + }); + + describe('when the email does not exist in db', () => { + it('should return 422', async () => { + const newCredentials = { + email: 'unregistered@user.com', + password: credentials.password, + }; + + await app + .inject({ + method: 'POST', + url: '/auth/login', + payload: newCredentials, + }) + .then((res) => { + expectError(new UnprocessableEntityException(), res.json); + }); + }); + }); + }); + + describe('when the password does not match in db', () => { + it('should return 422', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/login', + payload: { email: credentials.email, password: 'wrongPassword' }, + }) + .then((res) => { + expectError(new UnprocessableEntityException(), res.json); + }); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/register.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/register.e2e-spec.ts new file mode 100644 index 00000000..c21a12a3 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/auth/register.e2e-spec.ts @@ -0,0 +1,103 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { DatabaseService } from '@database/database.service'; +import { + BadRequestException, + ConflictException, + ValidationPipe, +} from '@nestjs/common'; +import { expectError } from '../utils/expect-error'; +import { MongoDBTestSetup } from '@test/utils'; + +describe('POST /auth/login', () => { + const credentials = { email: 'user@email.com', password: 'pass@ord' }; + + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + await app.init(); + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('given the email and password are valid', () => { + it('should create a new user and return a jwt token', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/register', + payload: credentials, + }) + .then((res) => { + expect(res.statusCode).toBe(200); + expect(typeof JSON.parse(res.body).idToken).toBe('string'); + }); + }); + + describe('when there is an existing user', () => { + it('should return 409 error', async () => { + const newCredentials = { + email: 'hello@email.com', + password: credentials.password, + }; + + await app + .inject({ + method: 'POST', + url: '/auth/register', + payload: newCredentials, + }) + .then((res) => { + expectError(new ConflictException(), res.json); + }); + }); + }); + }); + + describe('when there is invalid payload', () => { + it('should return 400 error', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/register', + payload: { + email: 'test', + password: 'test', + }, + }) + .then((res) => { + expectError(new BadRequestException(), res.json); + }); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/create.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/create.e2e-spec.ts new file mode 100644 index 00000000..8306b4b1 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/create.e2e-spec.ts @@ -0,0 +1,163 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + BadRequestException, + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { expectError } from '../utils/expect-error'; +import { DatabaseService } from '@database/database.service'; +import { MongoDBTestSetup } from '@test/utils'; + +describe('POST /v1/todos', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the created todo', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then(async (res) => { + const todo = await databaseService.connection + .collection('todos') + .findOne({ name: todoPayload.name }); + expect(res.statusCode).toEqual(201); + expect(res.payload).toEqual(`"${todo?._id}"`); + expect(todo?.note).toEqual(todoPayload.note); + expect(todo?.completed).toEqual(todoPayload.completed); + }); + }); + + it('should throw error if a todo with the same name already exists', async () => { + const { name, note, completed } = getTodoPayload(false); + await app.inject({ + method: 'POST', + url: '/v1/todos', + payload: { name, note, completed }, + }); + + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: { name, note, completed }, + }) + .then((res) => res.json()) + .then((response) => { + expect(response).toEqual({ + statusCode: 422, + message: 'Unprocessable Entity', + error: 'Unprocessable Entity', + }); + }); + }); + + it('should return 422 error when empty payload is provided', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: {}, + }) + .then((res) => expectError(new BadRequestException(), res.json)); + }); +}); + +describe('POST /v1/todos - real AuthGuard', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error when user is not authenticated', () => { + return app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-all.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-all.e2e-spec.ts new file mode 100644 index 00000000..476cf586 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-all.e2e-spec.ts @@ -0,0 +1,316 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { sortByField } from './utils/sortby-field-todos'; +import { SortOrder } from '@utils/query'; +import { + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { expectError } from '../utils/expect-error'; +import { DatabaseService } from '@database/database.service'; +import { MongoDBTestSetup } from '@test/utils'; + +describe('GET /v1/todos', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + }), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('when no filters are applied - should return whole todos list', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(3); + }); + }); + + it('should return completed todo list - when completed=true filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'true', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when completed=false filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'false', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return todo list with matched results - when name = "Laundry" filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos?filters[name]=Laundry', + query: { + name: 'Laundry 1', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '3', + pageNumber: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(0); + expect(total).toEqual(3); + }); + }); + + it('should return desc ordered todo list - when sort by name desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Desc), + ); + }); + + it('should return asc ordered todo list - when sort by name asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Asc), + ); + }); + + it('should return desc ordered todo list - when sort by createdAt desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Desc), + ); + }); + + it('should return asc ordered todo list - when sort by createdAt asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Asc), + ); + }); + + it('should return filtered todo list - when all filters are applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + pageNumber: '1', + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(2); + expect(total).toEqual(3); + }); + }); +}); + +describe('GET /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-one.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-one.e2e-spec.ts new file mode 100644 index 00000000..0463e612 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/find-one.e2e-spec.ts @@ -0,0 +1,145 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; +import { MongoDBTestSetup } from '@test/utils'; + +describe('GET /v1/todos/:id', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the todo - given todo id in the query', async () => { + const todo = ( + await databaseService.connection.collection('todos').find().toArray() + )[0]; + + await app + .inject({ + method: 'GET', + url: `/v1/todos/${todo._id}`, + }) + .then((res) => { + const { id, name, note, completed } = res.json(); + expect(res.statusCode).toBe(200); + + expect(id).toEqual(todo.id); + expect(name).toEqual(todo.name); + expect(note).toEqual(todo.note); + expect(completed).toEqual(todo.completed); + }); + }); + + it('should return 404', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'GET', + url: `/v1/todos/${objId}`, + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('GET /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'GET', + url: `/v1/todos/${objId}`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/remove.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/remove.e2e-spec.ts new file mode 100644 index 00000000..0604a6ca --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/remove.e2e-spec.ts @@ -0,0 +1,136 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { DatabaseService } from '@database/database.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError, MongoDBTestSetup } from '@test/utils'; +import { ObjectId } from 'mongodb'; + +describe('DELETE /v1/todos', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 200 when todo is deleted', async () => { + const todos = await databaseService.connection + .collection('todos') + .find() + .toArray(); + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/${todos[0]._id}`, + }) + .then(async ({ statusCode }) => { + expect(statusCode).toBe(200); + const todo = await databaseService.connection + .collection('todos') + .findOne({ _id: todos[0]._id }); + expect(todo).toBeNull(); + }); + }); + + it('should return 404 - non existing todo', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/${objId}`, + }) + .then((res) => expectError(new NotFoundException(), res.json)); + }); +}); + +describe('DELETE /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/update.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/update.e2e-spec.ts new file mode 100644 index 00000000..334ce60e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/update.e2e-spec.ts @@ -0,0 +1,182 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { DatabaseService } from '@database/database.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; +import { Todo } from '@api/todos/entities'; +import { ObjectId, WithId } from 'mongodb'; +import { MongoDBTestSetup } from '@test/utils'; + +describe('PUT /v1/todos/:id', () => { + let app: NestFastifyApplication; + let databaseService: DatabaseService; + let todo: WithId; + let databaseTestSetup: MongoDBTestSetup; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: databaseTestSetup.userId, email: 'hello@email' }; + return true; + }); + + todo = ( + await databaseService.connection + .collection('todos') + .find() + .toArray() + )[0]; + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the updated todo', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${todo._id}`, + payload: { + name: 'updated', + completed: true, + note: 'updated', + }, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.name).toEqual('updated'); + expect(responseBody.note).toEqual('updated'); + expect(responseBody.completed).toEqual(true); + }); + }); + + it('should return the not updated todo', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${todo._id}`, + payload: {}, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody._id).toEqual(todo._id.toString()); + expect(responseBody.name).toEqual(todo.name); + expect(responseBody.note).toEqual(todo.note); + expect(responseBody.completed).toEqual(todo.completed); + expect(responseBody.userId).toEqual(todo.userId.toString()); + }); + }); + + it('should return 404', async () => { + const objId = new ObjectId(1000); + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${objId}`, + payload: getTodoPayload(), + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('PUT /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let todo: WithId; + let databaseService: DatabaseService; + let databaseTestSetup: MongoDBTestSetup; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + databaseService = app.get(DatabaseService); + databaseTestSetup = new MongoDBTestSetup(databaseService.connection); + + todo = ( + await databaseService.connection + .collection('todos') + .find() + .toArray() + )[0]; + }); + + beforeEach(async () => { + await databaseTestSetup.removeSeededData(); + await databaseTestSetup.seedData(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/${todo._id}`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/get-todo-payload.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/get-todo-payload.ts new file mode 100644 index 00000000..d4923a3d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/get-todo-payload.ts @@ -0,0 +1,9 @@ +import { CreateTodoDto } from '@api/todos/dto/create-todo.dto'; + +export const getTodoPayload: (completed?: boolean) => CreateTodoDto = ( + completed, +) => ({ + name: 'Laundry' + Date.now(), + note: 'Buy detergent' + Date.now(), + completed: completed ?? false, +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/index.ts new file mode 100644 index 00000000..82299d0e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/index.ts @@ -0,0 +1,2 @@ +export * from './get-todo-payload'; +export * from './sortby-field-todos'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/sortby-field-todos.ts b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/sortby-field-todos.ts new file mode 100644 index 00000000..e6eb92c6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/mongodb/test/jwt/todos/utils/sortby-field-todos.ts @@ -0,0 +1,26 @@ +import { Todo } from '@api/todos/entities/todo.entity'; +import { SortOrder } from '@utils/query'; + +type ListTodoSortColumn = 'name' | 'createdAt'; + +export const sortByField: ( + list: Todo[], + field: ListTodoSortColumn, + order?: SortOrder +) => Todo[] = (list, field, order) => { + return list.sort((a, b) => { + if (order === 'desc') { + if (field === 'name') { + return b[field].localeCompare(a[field]); + } else { + return b[field] - a[field]; + } + } + + if (field === 'name') { + return a[field].localeCompare(b[field]); + } else { + return a[field] - b[field]; + } + }); +}; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.controller.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.controller.ts new file mode 100644 index 00000000..8e219388 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.controller.ts @@ -0,0 +1,20 @@ +import { Body, Controller, Inject, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { CredentialsDto } from './dto'; +import { AuthService } from './services/auth.service'; +import { Public } from '@utils/decorators'; + +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor( + @Inject(AuthService) + private readonly authService: AuthService, + ) {} + + @Public() + @Post('register') + register(@Body() credentials: CredentialsDto) { + return this.authService.register(credentials); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.module.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.module.ts new file mode 100644 index 00000000..86574070 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/auth.module.ts @@ -0,0 +1,30 @@ +import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; +import { APP_GUARD } from '@nestjs/core'; +import { AuthGuard } from './guards'; +import { HttpModule } from '@nestjs/axios'; +import { AuthController } from './auth.controller'; +import { UsersModule } from '@api/users/users.module'; +import { Auth0Service, AuthService, JwtStrategy } from './services'; + +export const AuthModuleMetadata = { + controllers: [AuthController], + imports: [ + HttpModule, + PassportModule.register({ defaultStrategy: 'jwt' }), + UsersModule, + ], + providers: [ + JwtStrategy, + { + provide: APP_GUARD, + useExisting: AuthGuard, + }, + AuthGuard, + Auth0Service, + AuthService, + ], +}; + +@Module(AuthModuleMetadata) +export class AuthModule {} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/credentials.dto.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/credentials.dto.ts new file mode 100644 index 00000000..bcfb161e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/credentials.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsEmail, + IsNotEmpty, + IsString, + MaxLength, + MinLength, +} from 'class-validator'; +import { LowerCase, Trim } from '@utils/class-transformers'; + +export class CredentialsDto { + @ApiProperty({ example: 'john@mail.com' }) + @Trim() + @LowerCase() + @IsEmail() + @MaxLength(255) + email: string; + + @ApiProperty({ example: 'MyS3cr37Pass' }) + @Trim() + @IsString() + @IsNotEmpty() + @MinLength(6) + @MaxLength(255) + password: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/index.ts new file mode 100644 index 00000000..9a2cf87c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/dto/index.ts @@ -0,0 +1 @@ +export * from './credentials.dto'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.spec.ts new file mode 100644 index 00000000..bb5eaf6e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.spec.ts @@ -0,0 +1,44 @@ +import { createMock } from '@golevelup/ts-jest'; +import { ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Test } from '@nestjs/testing'; +import { AuthGuard } from './auth.guard'; + +describe('Auth Guard', () => { + let authGuard: AuthGuard; + let reflector: Reflector; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [AuthGuard, Reflector], + }).compile(); + + authGuard = moduleRef.get(AuthGuard); + reflector = moduleRef.get(Reflector); + }); + + it('should be defined', () => { + expect(authGuard).toBeDefined(); + }); + + describe('canActivate', () => { + it('should return true if route is public', () => { + reflector.getAllAndOverride = jest.fn().mockReturnValue(true); + const mockContext = createMock(); + expect(authGuard.canActivate(mockContext)).toBe(true); + }); + + it("should call the super's object `canActivate` method", () => { + reflector.getAllAndOverride = jest.fn().mockReturnValue(false); + const superObj = Object.getPrototypeOf(Object.getPrototypeOf(authGuard)); + superObj.canActivate = jest.fn().mockReturnValue(true); + + const mockContext = createMock(); + const result = authGuard.canActivate(mockContext); + + expect(superObj.canActivate).toHaveBeenCalledTimes(1); + expect(superObj.canActivate).toHaveBeenCalledWith(mockContext); + expect(result).toBe(true); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.ts new file mode 100644 index 00000000..0fd9bbc1 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/auth.guard.ts @@ -0,0 +1,27 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_KEY } from '@utils/decorators/public.decorator'; +import { AuthGuard as PassportAuthGuard } from '@nestjs/passport'; + +@Injectable() +export class AuthGuard extends PassportAuthGuard('jwt') { + constructor(private reflector: Reflector) { + super(); + } + + private isPublic(context: ExecutionContext): boolean { + return this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + } + + canActivate(context: ExecutionContext) { + // Early return if the route is public + if (this.isPublic(context)) { + return true; + } + + return super.canActivate(context); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/index.ts new file mode 100644 index 00000000..b41e34a4 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/guards/index.ts @@ -0,0 +1 @@ +export * from './auth.guard'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/auth.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/auth.ts new file mode 100644 index 00000000..8ef3fbc7 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/auth.ts @@ -0,0 +1,32 @@ +export interface JwtClaims { + sub: string; + email: string; +} + +export interface UserData { + user: JwtClaims; +} + +export interface Credentials { + email: string; + password: string; +} + +export type Auth0User = { + blocked: boolean; + created_at: string; + email: string; + email_verified: boolean; + identities: { + connection: string; + user_id: string; + provider: string; + isSocial: boolean; + }[]; + name: string; + nickname: string; + picture: string; + updated_at: string; + user_id: string; + user_metadata: Record; +}; \ No newline at end of file diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/interfaces/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.spec.ts new file mode 100644 index 00000000..15776ad0 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.spec.ts @@ -0,0 +1,103 @@ +import { User } from '@api/users/entities'; +import { UsersRepository } from '@api/users/repositories'; +import { AuthService } from './auth.service'; +import { BadRequestException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModule } from '../auth.module'; +import { Auth0User, Credentials } from '../interfaces'; +import { Auth0Service } from './auth0.service'; +import { ConfigModule } from '@nestjs/config'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +const userCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const registeredUser: User = { + id: 1, + createdAt: Date.now().toString(), + updatedAt: Date.now().toString(), + email: userCreds.email, + password: 'very-secret', + userId: '1', +}; + +const auth0User: Auth0User = { + blocked: false, + created_at: Date.now().toString(), + email: userCreds.email, + email_verified: true, + identities: [], + name: 'userName', + nickname: 'userNickname', + picture: '', + updated_at: Date.now().toString(), + user_id: '1', + user_metadata: {}, +}; + +describe('AuthService', () => { + let authService: AuthService; + let auth0Service: Auth0Service; + let usersRepository: UsersRepository; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + AuthModule, + ConfigModule.forRoot({ + load: [authConfig, dbConfig, nodeConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + }).compile(); + + authService = module.get(AuthService); + auth0Service = module.get(Auth0Service); + usersRepository = module.get(UsersRepository); + + authService.logger.warn = jest.fn(); + authService.logger.error = jest.fn(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('register', () => { + it('when the email is not registered should register the user', async () => { + const createdUser = { ...registeredUser, ...userCreds }; + jest.spyOn(auth0Service, 'createUser').mockResolvedValueOnce(auth0User); + jest + .spyOn(usersRepository, 'insertOne') + .mockResolvedValueOnce(createdUser); + jest + .spyOn(auth0Service, 'updateUserMetadata') + .mockResolvedValueOnce(auth0User as any); + + const user = await authService.register(userCreds); + + expect(user).toEqual(createdUser); + }); + + it('throws error when creating a user in the database fails', async () => { + jest.spyOn(auth0Service, 'createUser').mockResolvedValueOnce(auth0User); + jest.spyOn(usersRepository, 'insertOne').mockRejectedValueOnce(undefined); + jest + .spyOn(auth0Service, 'deleteUser') + .mockResolvedValueOnce(auth0User as any); + + await expect(authService.register(userCreds)).rejects.toThrowError( + new BadRequestException('Something went wrong!'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.ts new file mode 100644 index 00000000..345ed7fc --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth.service.ts @@ -0,0 +1,40 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { CredentialsDto } from '../dto'; +import { UsersRepository } from '@api/users/repositories'; +import { Auth0Service } from './auth0.service'; + +@Injectable() +export class AuthService { + public logger = new Logger('AuthService'); + + constructor( + private usersRepository: UsersRepository, + private auth0Service: Auth0Service, + ) {} + + async register({ email, password }: CredentialsDto) { + const userAuth0 = await this.auth0Service.createUser(email, password); + + const user = await this.usersRepository + .insertOne({ + email, + userId: userAuth0.user_id, + }) + .catch(async (error) => { + this.logger.warn( + 'Creating a user in the database failed. Proceeding with deleting it in Auth0.', + ); + this.logger.error(error); + + await this.auth0Service.deleteUser(userAuth0.user_id); + + throw new BadRequestException('Something went wrong!'); + }); + + await this.auth0Service.updateUserMetadata(userAuth0.user_id, { + id: user.id, + }); + + return user; + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.spec.ts new file mode 100644 index 00000000..5d59b84d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.spec.ts @@ -0,0 +1,201 @@ +import { BadRequestException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModule } from '../auth.module'; +import { Auth0User, Credentials } from '../interfaces'; +import { Auth0Service } from './auth0.service'; +import { HttpService } from '@nestjs/axios'; +import { ConfigModule } from '@nestjs/config'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +const userCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const auth0User: Auth0User = { + blocked: false, + created_at: Date.now().toString(), + email: userCreds.email, + email_verified: true, + identities: [], + name: 'userName', + nickname: 'userNickname', + picture: '', + updated_at: Date.now().toString(), + user_id: '1', + user_metadata: {}, +}; + +describe('Auth0Service', () => { + let httpService: HttpService; + let auth0Service: Auth0Service; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [authConfig, dbConfig, nodeConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + AuthModule, + ], + }).compile(); + + auth0Service = module.get(Auth0Service); + httpService = module.get(HttpService); + + auth0Service.logger.warn = jest.fn(); + auth0Service.logger.error = jest.fn(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('createUser', () => { + it('when the email is not registered should register the user', async () => { + jest + .spyOn(auth0Service, 'searchUsersByEmail') + .mockResolvedValueOnce([] as unknown as Auth0User); + jest + .spyOn(httpService.axiosRef, 'post') + .mockResolvedValueOnce({ data: auth0User }); + + const user = await auth0Service.createUser( + userCreds.email, + userCreds.password, + ); + + expect(user).toEqual(auth0User); + }); + + it('throws error when user is already registered', async () => { + jest + .spyOn(auth0Service, 'searchUsersByEmail') + .mockResolvedValueOnce([auth0User] as unknown as Auth0User); + + await expect( + auth0Service.createUser(userCreds.email, userCreds.password), + ).rejects.toThrowError( + new BadRequestException('A user with this email already exists.'), + ); + }); + + it('throws error when auth0 request fails', async () => { + jest + .spyOn(auth0Service, 'searchUsersByEmail') + .mockResolvedValueOnce([] as unknown as Auth0User); + jest + .spyOn(httpService.axiosRef, 'post') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect( + auth0Service.createUser(userCreds.email, userCreds.password), + ).rejects.toThrowError(new BadRequestException()); + }); + }); + + it('updateUserMetadata updates metadata', async () => { + const newMetadata = { data: 'new' }; + jest + .spyOn(httpService.axiosRef, 'patch') + .mockResolvedValueOnce({ ...auth0User, user_metadata: newMetadata }); + + const user = await auth0Service.updateUserMetadata('1', newMetadata); + + expect(user).toEqual({ ...auth0User, user_metadata: newMetadata }); + }); + + describe('searchUsersByEmail', () => { + it('when the user is found', async () => { + jest + .spyOn(httpService.axiosRef, 'get') + .mockResolvedValueOnce({ data: auth0User }); + + const user = await auth0Service.searchUsersByEmail(auth0User.email); + + expect(user).toEqual(auth0User); + }); + + it('throws error when user is not found', async () => { + jest + .spyOn(httpService.axiosRef, 'get') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect( + auth0Service.searchUsersByEmail(auth0User.email), + ).rejects.toThrowError(new BadRequestException()); + }); + }); + + describe('searchUsersByEmail', () => { + it('when user is deleted', async () => { + jest.spyOn(httpService.axiosRef, 'delete').mockResolvedValueOnce({}); + + const response = await auth0Service.deleteUser(auth0User.user_id); + + expect(response).toEqual({}); + }); + + it('throws error when auth0 request fails', async () => { + jest + .spyOn(httpService.axiosRef, 'delete') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect( + auth0Service.deleteUser(auth0User.user_id), + ).rejects.toThrowError(new BadRequestException()); + }); + }); + + describe('onModuleInit', () => { + it('when accessToken is set', async () => { + const tokenResponse = { data: { access_token: 'token' } }; + jest + .spyOn(httpService.axiosRef, 'post') + .mockResolvedValueOnce(tokenResponse); + + const onModuleInit = jest.spyOn(auth0Service, 'onModuleInit'); + + auth0Service.onModuleInit(); + + expect(onModuleInit).toHaveBeenCalled(); + }); + + it('throws error accessToken is missing', async () => { + const tokenResponse = { data: {} }; + jest + .spyOn(httpService.axiosRef, 'post') + .mockResolvedValueOnce(tokenResponse); + + await expect(auth0Service.onModuleInit()).rejects.toThrowError( + new Error('Access token is missing!'), + ); + }); + + it('throws error when axios response is falsy(undefined)', async () => { + jest.spyOn(httpService.axiosRef, 'post').mockResolvedValueOnce(undefined); + + await expect(auth0Service.onModuleInit()).rejects.toThrowError( + new Error('Access token is missing!'), + ); + }); + + it('throws error when auth0 token request fails', async () => { + jest + .spyOn(httpService.axiosRef, 'post') + .mockRejectedValueOnce({ response: { data: {} } }); + + await expect(auth0Service.onModuleInit()).rejects.toThrowError( + new Error('Something went wrong!'), + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.ts new file mode 100644 index 00000000..4213c4f0 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/auth0.service.ts @@ -0,0 +1,136 @@ +import { HttpService } from '@nestjs/axios'; +import { + BadRequestException, + Inject, + Injectable, + Logger, + OnModuleInit, +} from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import { authConfig } from '@utils/environment'; +import { Auth0User } from '../interfaces'; + +@Injectable() +export class Auth0Service implements OnModuleInit { + public logger = new Logger('Auth0Service'); + + private accessToken = ''; + private baseURL = this.dbConfig.AUTH0_ISSUER_URL; + private AUTH0_CLIENT_ID = this.dbConfig.AUTH0_CLIENT_ID; + private AUTH0_CLIENT_SECRET = this.dbConfig.AUTH0_CLIENT_SECRET; + + constructor( + private httpService: HttpService, + @Inject(authConfig.KEY) + private dbConfig: ConfigType, + ) {} + + onModuleInit() { + return this.getAuth0AccessToken() + .then((token) => { + this.accessToken = token; + }) + .catch((err) => { + throw err; + }); + } + + private async getAuth0AccessToken() { + const response = await this.httpService.axiosRef + .post<{ access_token: string }>( + `${this.baseURL}oauth/token`, + new URLSearchParams({ + grant_type: 'client_credentials', + client_id: this.AUTH0_CLIENT_ID, + client_secret: this.AUTH0_CLIENT_SECRET, + audience: `${this.baseURL}api/v2/`, + }), + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + }, + ) + .catch((error) => { + this.logger.error(error.response.data); + throw new Error('Something went wrong!'); + }); + + if (!response?.data?.access_token) { + this.logger.error('Access token is missing in the response data'); + throw new Error('Access token is missing!'); + } + + return response.data.access_token; + } + + private buildHeaders() { + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: `Bearer ${this.accessToken}`, + }; + } + + public async createUser(email: string, password: string) { + const users = await this.searchUsersByEmail(email); + if (Array.isArray(users) && users.length) { + throw new BadRequestException('A user with this email already exists.'); + } + + return this.httpService.axiosRef + .post( + `${this.baseURL}api/v2/users`, + { + email, + user_metadata: {}, + connection: 'Username-Password-Authentication', + password, + verify_email: true, + }, + { headers: this.buildHeaders() }, + ) + .then(({ data }) => data) + .catch((error) => { + this.logger.error(error.response.data); + throw new BadRequestException(); + }); + } + + public updateUserMetadata( + userId: string, + metadata: Auth0User['user_metadata'], + ) { + return this.httpService.axiosRef.patch( + `${this.baseURL}api/v2/users/${userId}`, + { + user_metadata: metadata, + }, + { headers: this.buildHeaders() }, + ); + } + + public searchUsersByEmail(email: string) { + return this.httpService.axiosRef + .get(`${this.baseURL}api/v2/users-by-email`, { + params: { email }, + headers: this.buildHeaders(), + }) + .then(({ data }) => data) + .catch((error) => { + this.logger.error(error.response.data); + throw new BadRequestException(); + }); + } + + public deleteUser(userId: string) { + return this.httpService.axiosRef + .delete(`${this.baseURL}api/v2/users/${userId}`, { + headers: this.buildHeaders(), + }) + .catch((error) => { + this.logger.error(error.response.data); + throw new BadRequestException(); + }); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/index.ts new file mode 100644 index 00000000..6a299d20 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/index.ts @@ -0,0 +1,3 @@ +export * from './auth.service'; +export * from './jwt.strategy'; +export * from './auth0.service'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.spec.ts new file mode 100644 index 00000000..5b8970a9 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.spec.ts @@ -0,0 +1,62 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModule } from '../auth.module'; +import { Auth0User, Credentials } from '../interfaces'; +import { ConfigModule } from '@nestjs/config'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; +import { JwtStrategy } from './jwt.strategy'; + +const userCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const auth0User: Auth0User = { + blocked: false, + created_at: Date.now().toString(), + email: userCreds.email, + email_verified: true, + identities: [], + name: 'userName', + nickname: 'userNickname', + picture: '', + updated_at: Date.now().toString(), + user_id: '1', + user_metadata: {}, +}; + +describe('JWT Strategy', () => { + let jwtStrategy: JwtStrategy; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [authConfig, dbConfig, nodeConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + AuthModule, + ], + }).compile(); + + jwtStrategy = module.get(JwtStrategy); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('validate', () => { + it('should return the Auth0User', () => { + const result = jwtStrategy.validate(auth0User); + + expect(result).toEqual(auth0User); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.ts new file mode 100644 index 00000000..5f6c9c0c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/auth0/services/jwt.strategy.ts @@ -0,0 +1,33 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { passportJwtSecret } from 'jwks-rsa'; +import { Auth0User } from '../interfaces'; +import { authConfig } from '@utils/environment'; +import { ConfigType } from '@nestjs/config'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor( + @Inject(authConfig.KEY) + config: ConfigType, + ) { + super({ + secretOrKeyProvider: passportJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `${config.AUTH0_ISSUER_URL}.well-known/jwks.json`, + }), + + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + audience: config.AUTH0_AUDIENCE, + issuer: `${config.AUTH0_ISSUER_URL}`, + algorithms: ['RS256'], + }); + } + + validate(payload: Auth0User): Auth0User { + return payload; + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.controller.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.controller.ts new file mode 100644 index 00000000..4011277f --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.controller.ts @@ -0,0 +1,67 @@ +import { Body, Controller, HttpCode, Inject, Post } from '@nestjs/common'; +import { + ApiBody, + ApiConflictResponse, + ApiOkResponse, + ApiOperation, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { CredentialsDto } from './dto'; +import { AuthService } from './services'; +import { Public } from '@utils/decorators'; +import { JwtToken } from './entities'; +import { Errors } from '@utils/enums'; +import { ConflictDto, UnprocessableEntityDto } from '@utils/dtos'; + +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor( + @Inject(AuthService) + private readonly authService: AuthService, + ) {} + + @Post('register') + @Public() + @HttpCode(200) + @ApiOperation({ + summary: 'Register a user', + description: 'Register a user', + }) + @ApiBody({ type: CredentialsDto }) + @ApiOkResponse({ + description: 'OK', + type: JwtToken, + }) + @ApiConflictResponse({ + description: Errors.Conflict, + type: ConflictDto, + }) + @ApiUnprocessableEntityResponse({ + description: Errors.UnprocessableEntity, + type: UnprocessableEntityDto, + }) + register(@Body() credentials: CredentialsDto): Promise { + return this.authService.register(credentials); + } + + @Post('login') + @Public() + @ApiOperation({ + summary: 'Login a user', + description: 'Authenticate a user', + }) + @ApiBody({ type: CredentialsDto }) + @ApiOkResponse({ + description: 'OK', + type: JwtToken, + }) + @ApiUnprocessableEntityResponse({ + description: Errors.UnprocessableEntity, + type: UnprocessableEntityDto, + }) + async login(@Body() credentials: CredentialsDto): Promise { + return this.authService.login(credentials); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.module.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.module.ts new file mode 100644 index 00000000..bb469a3b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/auth.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './auth.controller'; +import { AuthService, JwtService, PasswordService } from './services'; +import { ConfigModule } from '@nestjs/config'; +import { UsersModule } from '@api/users/users.module'; +import { APP_GUARD } from '@nestjs/core'; +import { AuthGuard } from './guards'; + +export const AuthModuleMetadata = { + imports: [ConfigModule, UsersModule], + providers: [ + JwtService, + PasswordService, + AuthService, + { + provide: APP_GUARD, + useExisting: AuthGuard, + }, + AuthGuard, + ], + controllers: [AuthController], +}; + +@Module(AuthModuleMetadata) +export class AuthModule {} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/credentials.dto.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/credentials.dto.ts new file mode 100644 index 00000000..bcfb161e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/credentials.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsEmail, + IsNotEmpty, + IsString, + MaxLength, + MinLength, +} from 'class-validator'; +import { LowerCase, Trim } from '@utils/class-transformers'; + +export class CredentialsDto { + @ApiProperty({ example: 'john@mail.com' }) + @Trim() + @LowerCase() + @IsEmail() + @MaxLength(255) + email: string; + + @ApiProperty({ example: 'MyS3cr37Pass' }) + @Trim() + @IsString() + @IsNotEmpty() + @MinLength(6) + @MaxLength(255) + password: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/index.ts new file mode 100644 index 00000000..9a2cf87c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/dto/index.ts @@ -0,0 +1 @@ +export * from './credentials.dto'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/index.ts new file mode 100644 index 00000000..799fdad6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/index.ts @@ -0,0 +1 @@ +export * from './jwt-token.entity'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/jwt-token.entity.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/jwt-token.entity.ts new file mode 100644 index 00000000..dad4d0b9 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/entities/jwt-token.entity.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class JwtToken { + @ApiProperty() + idToken: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.spec.ts new file mode 100644 index 00000000..2c6582fe --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.spec.ts @@ -0,0 +1,89 @@ +import { createMock } from '@golevelup/ts-jest'; +import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import jwt from 'jsonwebtoken'; +import { Test } from '@nestjs/testing'; +import { AuthGuard } from './auth.guard'; +import { JwtClaims } from '../interfaces'; +import { authConfig } from '@utils/environment'; + +describe('Auth Guard', () => { + let authGuard: AuthGuard; + let reflector: Reflector; + + const env: { [key: string]: string } = { + JWT_SECRET: 'very-secret', + JWT_EXPIRATION: '1d', + }; + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + AuthGuard, + Reflector, + { + provide: authConfig.KEY, + useValue: env, + }, + ], + }).compile(); + + authGuard = moduleRef.get(AuthGuard); + reflector = moduleRef.get(Reflector); + }); + + it('should be defined', () => { + expect(authGuard).toBeDefined(); + }); + + describe('canActivate', () => { + const testClaims: JwtClaims = { + sub: 'tz4a98xxat96iws9zmbrgj3a', + email: 'admin@admin.com', + }; + + it('should return true if route is public', () => { + reflector.getAllAndOverride = jest.fn().mockReturnValue(true); + const mockContext = createMock(); + expect(authGuard.canActivate(mockContext)).toBe(true); + }); + + it('should return true when user is authenticated', () => { + const mockContext = createMock(); + mockContext.switchToHttp().getRequest.mockReturnValue({ + headers: { + authorization: 'Bearer testToken', + }, + }); + jest.spyOn(jwt, 'verify').mockReturnValue(testClaims as never); + + expect(authGuard.canActivate(mockContext)).toBe(true); + }); + + it('should throw an Unauthorized error when user is not authenticated', () => { + const mockContext = createMock(); + mockContext.switchToHttp().getRequest.mockReturnValue({ + headers: { + authorization: 'Bearer invalidToken', + }, + }); + jest.spyOn(jwt, 'verify').mockImplementation(() => { + throw new Error(); + }); + + expect(() => authGuard.canActivate(mockContext)).toThrow( + UnauthorizedException, + ); + }); + + it('should throw an Unauthorized error when token is not send', () => { + const mockContext = createMock(); + mockContext.switchToHttp().getRequest.mockReturnValue({ + headers: {}, + }); + + expect(() => authGuard.canActivate(mockContext)).toThrow( + UnauthorizedException, + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.ts new file mode 100644 index 00000000..4d509e82 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/auth.guard.ts @@ -0,0 +1,59 @@ +import { + CanActivate, + ExecutionContext, + Inject, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_KEY } from '@utils/decorators/public.decorator'; +import { Request } from 'express-jwt'; +import { verify } from 'jsonwebtoken'; +import { JwtClaims } from '../interfaces'; +import { authConfig } from '@utils/environment'; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor( + @Inject(authConfig.KEY) private auth: ConfigType, + private reflector: Reflector, + ) {} + + canActivate(context: ExecutionContext): boolean { + // Early return if the route is public + if (this.isPublic(context)) { + return true; + } + + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + if (!token) { + throw new UnauthorizedException(); + } + + try { + const { sub, email } = verify( + token, + this.auth.JWT_SECRET, + ) as Partial; + request.user = { sub, email } as JwtClaims; + } catch { + throw new UnauthorizedException(); + } + + return true; + } + + private extractTokenFromHeader(request: Request): string | undefined { + const [type, token] = request.headers.authorization?.split(' ') ?? []; + return type === 'Bearer' ? token : undefined; + } + + private isPublic(context: ExecutionContext): boolean { + return this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/index.ts new file mode 100644 index 00000000..b41e34a4 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/guards/index.ts @@ -0,0 +1 @@ +export * from './auth.guard'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/auth.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/auth.ts new file mode 100644 index 00000000..370a4d83 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/auth.ts @@ -0,0 +1,13 @@ +export interface JwtClaims { + sub: string; + email: string; +} + +export interface UserData { + user: JwtClaims; +} + +export interface Credentials { + email: string; + password: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/interfaces/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.spec.ts new file mode 100644 index 00000000..5799b204 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.spec.ts @@ -0,0 +1,138 @@ +import { User } from '@api/users/entities'; +import { UsersRepository } from '@api/users/repositories'; +import { JwtService } from './jwt.service'; +import { PasswordService } from './password.service'; +import { AuthService } from './auth.service'; +import { + ConflictException, + UnprocessableEntityException, +} from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthModule } from '../auth.module'; +import { Credentials } from '../interfaces'; +import { ConfigModule } from '@nestjs/config'; +import { authConfig, dbConfig, nodeConfig } from '@utils/environment'; + +const registeredUser: User = { + id: 1, + userId: 'tz4a98xxat96iws9zmbrgj3a', + createdAt: Date.now().toString(), + updatedAt: Date.now().toString(), + email: 'registered-email@example.com', + password: 'very-secret', +}; + +const unregisteredCreds: Credentials = { + email: 'new-email@example.com', + password: 'very-secret', +}; + +const registeredCreds: Credentials = { + email: 'registered@email.com', + password: 'very-secret', +}; + +describe('AuthService', () => { + let authService: AuthService; + let jwtService: JwtService; + let passwordService: PasswordService; + let usersRepository: UsersRepository; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + AuthModule, + ConfigModule.forRoot({ + load: [authConfig, dbConfig, nodeConfig], + isGlobal: true, + ignoreEnvFile: true, + }), + ], + }).compile(); + + authService = module.get(AuthService); + jwtService = module.get(JwtService); + passwordService = module.get(PasswordService); + usersRepository = module.get(UsersRepository); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('register', () => { + it('when the email is not registered should register the user', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(undefined); + jest + .spyOn(usersRepository, 'insertOne') + .mockResolvedValueOnce({ ...registeredUser, ...unregisteredCreds }); + jest + .spyOn(usersRepository, 'updateOne') + .mockResolvedValueOnce({ ...registeredUser, ...unregisteredCreds }); + jest.spyOn(jwtService, 'sign').mockReturnValueOnce('jwtTokenValue'); + + const response = await authService.register(unregisteredCreds); + + expect(response.idToken).toBe('jwtTokenValue'); + }); + + it('when the email is already registered should throw an error', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(registeredUser); + + await expect( + authService.register({ + email: registeredUser.email, + password: registeredUser.password!, + }), + ).rejects.toThrowError(new ConflictException('User email already taken')); + }); + }); + + describe('login', () => { + it('when the email is not registered should throw an error', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(undefined); + + await expect(authService.login(unregisteredCreds)).rejects.toThrowError( + new UnprocessableEntityException('Invalid email or password'), + ); + }); + + it('when the email is registered and the password does not match should return an error', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(registeredUser); + + jest.spyOn(passwordService, 'compare').mockResolvedValueOnce(false); + + await expect(authService.login(registeredCreds)).rejects.toThrowError( + new UnprocessableEntityException('Invalid email or password'), + ); + }); + + it('when the email is registered and the email and password are valid should log the user', async () => { + jest + .spyOn(usersRepository, 'findByEmail') + .mockResolvedValueOnce(registeredUser); + + jest.spyOn(passwordService, 'compare').mockResolvedValueOnce(true); + + jest.spyOn(jwtService, 'sign').mockReturnValueOnce('jwtTokenValue'); + + const response = await authService.login(registeredCreds); + + expect(response.idToken).toBe('jwtTokenValue'); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.ts new file mode 100644 index 00000000..fc82d411 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/auth.service.ts @@ -0,0 +1,64 @@ +import { JwtToken } from '../entities'; +import { + ConflictException, + Inject, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; +import { UsersRepository } from '@api/users/repositories'; +import { JwtService } from './jwt.service'; +import { PasswordService } from './password.service'; +import { Credentials } from '../interfaces'; + +@Injectable() +export class AuthService { + constructor( + @Inject(UsersRepository) + private readonly users: UsersRepository, + @Inject(JwtService) + private readonly jwt: JwtService, + @Inject(PasswordService) + private readonly password: PasswordService + ) {} + + async register({ email, password }: Credentials): Promise { + const existingUser = await this.users.findByEmail(email); + if (existingUser) { + throw new ConflictException('User email already taken'); + } + + const { id } = await this.users.insertOne({ + email, + password: await this.password.hash(password), + }); + + const user = await this.users.updateOne(id, { userId: id.toString() }); + + return { + idToken: this.jwt.sign({ sub: user.userId, email }), + }; + } + + async login({ email, password }: Credentials): Promise { + const user = await this.users.findByEmail(email); + + if (!user) { + throw new UnprocessableEntityException('Invalid email or password'); + } + + const passwordMatches = await this.password.compare( + password, + user.password! + ); + + if (!passwordMatches) { + throw new UnprocessableEntityException('Invalid email or password'); + } + + const token = this.jwt.sign({ sub: user.userId, email }); + + return { + idToken: token, + }; + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/index.ts new file mode 100644 index 00000000..a3c1b63b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/index.ts @@ -0,0 +1,3 @@ +export * from './auth.service'; +export * from './jwt.service'; +export * from './password.service'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.spec.ts new file mode 100644 index 00000000..0bc184ea --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.spec.ts @@ -0,0 +1,55 @@ +import { verify } from 'jsonwebtoken'; +import { JwtService } from './jwt.service'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { Test } from '@nestjs/testing'; +import { nodeConfig, dbConfig, authConfig } from '@utils/environment'; + +describe('JwtService', () => { + const env: { [key: string]: string } = { + JWT_SECRET: 'very-secret', + JWT_EXPIRATION: '1d', + }; + + let jwtService: JwtService; + + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [nodeConfig, dbConfig, authConfig], + cache: true, + ignoreEnvFile: true, + isGlobal: true, + }), + ], + providers: [ + { + provide: authConfig.KEY, + useValue: env, + }, + JwtService, + ], + }).compile(); + + jwtService = moduleRef.get(JwtService); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('sign', () => { + it('should sign the claims returning a JWT token', () => { + const claims = { sub: '1', email: 'email@example.com' }; + const token = jwtService.sign(claims); + const payload = verify(token, env.JWT_SECRET); + expect(payload).toEqual(expect.objectContaining(claims)); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.ts new file mode 100644 index 00000000..1e481cfc --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/jwt.service.ts @@ -0,0 +1,18 @@ +import { sign } from 'jsonwebtoken'; +import { Injectable, Inject } from '@nestjs/common'; +import { JwtClaims } from '../interfaces'; +import { authConfig } from '@utils/environment'; +import { ConfigType } from '@nestjs/config'; + +@Injectable() +export class JwtService { + constructor( + @Inject(authConfig.KEY) private auth: ConfigType, + ) {} + + sign(claims: JwtClaims): string { + return sign(claims, this.auth.JWT_SECRET, { + expiresIn: this.auth.JWT_EXPIRATION, + }); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.spec.ts new file mode 100644 index 00000000..6f6c1f26 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.spec.ts @@ -0,0 +1,38 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PasswordService } from './password.service'; + +describe('PasswordService', () => { + let passwordsService: PasswordService; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + providers: [PasswordService], + }).compile(); + + passwordsService = app.get(PasswordService); + }); + + describe('hash', () => { + it('should return a different string', async () => { + expect(await passwordsService.hash('some-text')).not.toBe('some-text'); + }); + }); + + describe('compare', () => { + it('should be true when the hash is of the value', async () => { + const password = 'very-secret'; + const hash = await passwordsService.hash(password); + + expect(await passwordsService.compare(password, hash)).toBe(true); + }); + + it('should be false when the hash is not of the value', async () => { + const password = 'very-secret'; + const hash = await passwordsService.hash(password); + + expect(await passwordsService.compare('not-my-password', hash)).toBe( + false, + ); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.ts new file mode 100644 index 00000000..7003fc54 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/jwt/services/password.service.ts @@ -0,0 +1,13 @@ +import { hash, compare, genSalt } from 'bcrypt'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class PasswordService { + async hash(password: string): Promise { + return hash(password, await genSalt()); + } + + async compare(password: string, hash: string): Promise { + return compare(password, hash); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/jest-e2e.config.js b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/jest-e2e.config.js new file mode 100644 index 00000000..bc876b71 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/jest-e2e.config.js @@ -0,0 +1,43 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '..', + testEnvironment: 'node', + testRegex: '.e2e-spec.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + coverageDirectory: 'coverage-e2e', + collectCoverageFrom: ['/src/api/**/*.(t|j)s'], + coveragePathIgnorePatterns: [ + '/src/main.ts$', + '/.*spec.ts$', + '/.*module.ts$', + '/src/.*/index.ts$', + '/src/.*dto.ts$', + '/src/.*entity.ts$', + '/src/.*repository.ts$', + '/src/.*guard.ts$', + '/src/api/auth/.*.ts$', + ], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + setupFiles: ['/jest.setup.ts'], + // path aliases from tsconfig.json + moduleNameMapper: { + '^@api/(.*)$': '/src/api/$1', + '^@database$': '/src/database', + '^@database/(.*)$': '/src/database/$1', + '^@extensions/(.*)$': '/src/extensions/$1', + '^@middleware$': '/src/middleware', + '^@utils/(.*)$': '/src/utils/$1', + }, +}; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/create.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/create.e2e-spec.ts new file mode 100644 index 00000000..94c17ced --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/create.e2e-spec.ts @@ -0,0 +1,167 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + BadRequestException, + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { NestKnexService } from '@database/nest-knex.service'; +import { expectError } from '../utils/expect-error'; +import { Auth0Service } from '@api/auth/services'; + +describe('POST /v1/todos', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the created todo', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then((res) => { + const responseBody = res.json(); + expect(responseBody.name).toEqual(todoPayload.name); + expect(responseBody.note).toEqual(todoPayload.note); + expect(responseBody.completed).toEqual(todoPayload.completed); + }); + }); + + it('should throw error if a todo with the same name already exists', async () => { + const { name, note, completed } = await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: getTodoPayload(false), + }) + .then((res) => res.json()); + + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: { name, note, completed }, + }) + .then((res) => res.json()) + .then((response) => { + expect(response).toEqual({ + statusCode: 422, + message: 'Unprocessable Entity', + error: 'Unprocessable Entity', + }); + }); + }); + + it('should return 422 error when empty payload is provided', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: {}, + }) + .then((res) => expectError(new BadRequestException(), res.json)); + }); +}); + +describe('POST /v1/todos - real AuthGuard', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error when user is not authenticated', () => { + return app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-all.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-all.e2e-spec.ts new file mode 100644 index 00000000..7f86fd30 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-all.e2e-spec.ts @@ -0,0 +1,321 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { sortByField } from './utils/sortby-field-todos'; +import { SortOrder } from '@utils/query'; +import { + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { NestKnexService } from '@database/nest-knex.service'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { expectError } from '../utils/expect-error'; +import { Auth0Service } from '@api/auth/services'; + +describe('GET /v1/todos', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + }) + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('when no filters are applied - should return whole todos list', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(3); + }); + }); + + it('should return completed todo list - when completed=true filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'true', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when completed=false filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'false', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return todo list with matched results - when name = "Laundry" filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos?filters[name]=Laundry', + query: { + name: 'Laundry 1', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '3', + pageNumber: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(0); + expect(total).toEqual(3); + }); + }); + + it('should return desc ordered todo list - when sort by name desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Desc) + ); + }); + + it('should return asc ordered todo list - when sort by name asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Asc) + ); + }); + + it('should return desc ordered todo list - when sort by createdAt desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Desc) + ); + }); + + it('should return asc ordered todo list - when sort by createdAt asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Asc) + ); + }); + + it('should return filtered todo list - when all filters are applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + pageNumber: '1', + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(2); + expect(total).toEqual(3); + }); + }); +}); + +describe('GET /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-one.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-one.e2e-spec.ts new file mode 100644 index 00000000..f603e1ea --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/find-one.e2e-spec.ts @@ -0,0 +1,148 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; +import { Auth0Service } from '@api/auth/services'; + +describe('GET /v1/todos/:id', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the todo - given todo id in the query', async () => { + const todo = await nestKnexService + .connection('todos') + .where({ id: 1 }) + .first(); + + await app + .inject({ + method: 'GET', + url: `/v1/todos/1`, + }) + .then((res) => { + const { id, name, note, completed } = res.json(); + expect(res.statusCode).toBe(200); + + expect(id).toEqual(todo.id); + expect(name).toEqual(todo.name); + expect(note).toEqual(todo.note); + expect(completed).toEqual(todo.completed); + }); + }); + + it('should return 404', async () => { + await app + .inject({ + method: 'GET', + url: `/v1/todos/${Date.now()}`, + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('GET /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos/1', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/remove.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/remove.e2e-spec.ts new file mode 100644 index 00000000..a1c50c35 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/remove.e2e-spec.ts @@ -0,0 +1,139 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; +import { Auth0Service } from '@api/auth/services'; + +describe('DELETE /v1/todos', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 200 when todo is deleted', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1`, + }) + .then(async ({ statusCode }) => { + expect(statusCode).toBe(200); + const todo = await nestKnexService + .connection('todos') + .where({ id: 1 }) + .first(); + expect(todo).toBeUndefined(); + }); + }); + + it('should return 404 - non existing todo', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1000`, + }) + .then((res) => expectError(new NotFoundException(), res.json)); + }); +}); + +describe('DELETE /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/update.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/update.e2e-spec.ts new file mode 100644 index 00000000..b6467d48 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/update.e2e-spec.ts @@ -0,0 +1,172 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; +import { Auth0Service } from '@api/auth/services'; + +describe('PUT /v1/todos/:id', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the updated todo', async () => { + await app + .inject({ + method: 'PUT', + url: '/v1/todos/1', + payload: { + name: 'updated', + completed: true, + note: 'updated', + }, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.name).toEqual('updated'); + expect(responseBody.note).toEqual('updated'); + expect(responseBody.completed).toEqual(true); + }); + }); + + it('should return the not updated todo', async () => { + const todo = await nestKnexService + .connection('todos') + .where({ id: 1 }) + .first(); + + await app + .inject({ + method: 'PUT', + url: `/v1/todos/1`, + payload: {}, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.id).toEqual(todo.id); + expect(responseBody.name).toEqual(todo.name); + expect(responseBody.note).toEqual(todo.note); + expect(responseBody.completed).toEqual(todo.completed); + expect(responseBody.userId).toEqual(todo.userId); + }); + }); + + it('should return 404', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/32131`, + payload: getTodoPayload(), + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('PUT /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + class Auth0ServiceMock {} + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(Auth0Service) + .useClass(Auth0ServiceMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'PUT', + url: '/v1/todos/1', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/get-todo-payload.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/get-todo-payload.ts new file mode 100644 index 00000000..d4923a3d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/get-todo-payload.ts @@ -0,0 +1,9 @@ +import { CreateTodoDto } from '@api/todos/dto/create-todo.dto'; + +export const getTodoPayload: (completed?: boolean) => CreateTodoDto = ( + completed, +) => ({ + name: 'Laundry' + Date.now(), + note: 'Buy detergent' + Date.now(), + completed: completed ?? false, +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/index.ts new file mode 100644 index 00000000..82299d0e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/index.ts @@ -0,0 +1,2 @@ +export * from './get-todo-payload'; +export * from './sortby-field-todos'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/sortby-field-todos.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/sortby-field-todos.ts new file mode 100644 index 00000000..7d703492 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/auth0/todos/utils/sortby-field-todos.ts @@ -0,0 +1,17 @@ +import { Todo } from '@api/todos/entities/todo.entity'; +import { SortOrder } from '@utils/query'; + +type ListTodoSortColumn = 'name' | 'createdAt'; + +export const sortByField: ( + list: Todo[], + field: ListTodoSortColumn, + order?: SortOrder, +) => Todo[] = (list, field, order) => { + return list.sort((a, b) => { + if (order === 'desc') { + return b[field].localeCompare(a[field]); + } + return a[field].localeCompare(b[field]); + }); +}; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/login.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/login.e2e-spec.ts new file mode 100644 index 00000000..b7ae7920 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/login.e2e-spec.ts @@ -0,0 +1,111 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { expectError } from '../utils/expect-error'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + BadRequestException, + UnprocessableEntityException, + ValidationPipe, +} from '@nestjs/common'; + +describe('POST /auth/login', () => { + const credentials = { email: 'hello@email.com', password: 'pass@ord' }; + + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter() + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }) + ); + + await app.init(); + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('given the email and password are valid', () => { + it('should login the user and return a jwt token', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/login', + payload: credentials, + }) + .then((res) => { + expect(res.statusCode).toBe(201); + expect(typeof res.json().idToken).toBe('string'); + }); + }); + + describe('when there are empty credentials', () => { + it('should return 400', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/login', + }) + .then((res) => { + expect(expectError(new BadRequestException(), res.json)); + }); + }); + }); + + describe('when the email does not exist in db', () => { + it('should return 422', async () => { + const newCredentials = { + email: 'unregistered@user.com', + password: credentials.password, + }; + + await app + .inject({ + method: 'POST', + url: '/auth/login', + payload: newCredentials, + }) + .then((res) => { + expectError(new UnprocessableEntityException(), res.json); + }); + }); + }); + }); + + describe('when the password does not match in db', () => { + it('should return 422', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/login', + payload: { email: credentials.email, password: 'wrongPassword' }, + }) + .then((res) => { + expectError(new UnprocessableEntityException(), res.json); + }); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/register.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/register.e2e-spec.ts new file mode 100644 index 00000000..9369cb1e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/auth/register.e2e-spec.ts @@ -0,0 +1,101 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + BadRequestException, + ConflictException, + ValidationPipe, +} from '@nestjs/common'; +import { expectError } from '../utils/expect-error'; + +describe('POST /auth/login', () => { + const credentials = { email: 'user@email.com', password: 'pass@ord' }; + + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + await app.init(); + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('given the email and password are valid', () => { + it('should create a new user and return a jwt token', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/register', + payload: credentials, + }) + .then((res) => { + expect(res.statusCode).toBe(200); + expect(typeof JSON.parse(res.body).idToken).toBe('string'); + }); + }); + + describe('when there is an existing user', () => { + it('should return 409 error', async () => { + const newCredentials = { + email: 'hello@email.com', + password: credentials.password, + }; + + await app + .inject({ + method: 'POST', + url: '/auth/register', + payload: newCredentials, + }) + .then((res) => { + expectError(new ConflictException(), res.json); + }); + }); + }); + }); + + describe('when there is invalid payload', () => { + it('should return 400 error', async () => { + await app + .inject({ + method: 'POST', + url: '/auth/register', + payload: { + email: 'test', + password: 'test', + }, + }) + .then((res) => { + expectError(new BadRequestException(), res.json); + }); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/create.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/create.e2e-spec.ts new file mode 100644 index 00000000..99f974a5 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/create.e2e-spec.ts @@ -0,0 +1,157 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { + BadRequestException, + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { NestKnexService } from '@database/nest-knex.service'; +import { expectError } from '../utils/expect-error'; + +describe('POST /v1/todos', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the created todo', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then((res) => { + const responseBody = res.json(); + expect(responseBody.name).toEqual(todoPayload.name); + expect(responseBody.note).toEqual(todoPayload.note); + expect(responseBody.completed).toEqual(todoPayload.completed); + }); + }); + + it('should throw error if a todo with the same name already exists', async () => { + const { name, note, completed } = await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: getTodoPayload(false), + }) + .then((res) => res.json()); + + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: { name, note, completed }, + }) + .then((res) => res.json()) + .then((response) => { + expect(response).toEqual({ + statusCode: 422, + message: 'Unprocessable Entity', + error: 'Unprocessable Entity', + }); + }); + }); + + it('should return 422 error when empty payload is provided', async () => { + await app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: {}, + }) + .then((res) => expectError(new BadRequestException(), res.json)); + }); +}); + +describe('POST /v1/todos - real AuthGuard', () => { + const todoPayload = getTodoPayload(); + + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error when user is not authenticated', () => { + return app + .inject({ + method: 'POST', + url: '/v1/todos', + payload: todoPayload, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-all.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-all.e2e-spec.ts new file mode 100644 index 00000000..e5afe87d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-all.e2e-spec.ts @@ -0,0 +1,311 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { sortByField } from './utils/sortby-field-todos'; +import { SortOrder } from '@utils/query'; +import { + ExecutionContext, + UnauthorizedException, + ValidationPipe, +} from '@nestjs/common'; +import { NestKnexService } from '@database/nest-knex.service'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { expectError } from '../utils/expect-error'; + +describe('GET /v1/todos', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + }), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('when no filters are applied - should return whole todos list', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(3); + }); + }); + + it('should return completed todo list - when completed=true filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'true', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when completed=false filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + completed: 'false', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return todo list with matched results - when name = "Laundry" filter is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos?filters[name]=Laundry', + query: { + name: 'Laundry 1', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(1); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items }) => { + expect(items.length).toEqual(2); + }); + }); + + it('should return not completed todo list - when pagination items is applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + pageSize: '3', + pageNumber: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(0); + expect(total).toEqual(3); + }); + }); + + it('should return desc ordered todo list - when sort by name desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Desc), + ); + }); + + it('should return asc ordered todo list - when sort by name asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'name', SortOrder.Asc), + ); + }); + + it('should return desc ordered todo list - when sort by createdAt desc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'desc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Desc), + ); + }); + + it('should return asc ordered todo list - when sort by createdAt asc', async () => { + const response = await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => res.json()); + + const sortedRes = await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'createdAt', + order: 'asc', + }, + }) + .then((res) => res.json()); + + expect(sortedRes.total).toEqual(response.total); + expect(sortedRes.items).toStrictEqual( + sortByField(response.items, 'createdAt', SortOrder.Asc), + ); + }); + + it('should return filtered todo list - when all filters are applied', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + query: { + column: 'name', + order: 'desc', + pageNumber: '1', + pageSize: '2', + }, + }) + .then((res) => res.json()) + .then(({ items, total }) => { + expect(items.length).toEqual(2); + expect(total).toEqual(3); + }); + }); +}); + +describe('GET /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-one.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-one.e2e-spec.ts new file mode 100644 index 00000000..9284b346 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/find-one.e2e-spec.ts @@ -0,0 +1,138 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; + +describe('GET /v1/todos/:id', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the todo - given todo id in the query', async () => { + const todo = await nestKnexService + .connection('todos') + .where({ id: 1 }) + .first(); + + await app + .inject({ + method: 'GET', + url: `/v1/todos/1`, + }) + .then((res) => { + const { id, name, note, completed } = res.json(); + expect(res.statusCode).toBe(200); + + expect(id).toEqual(todo.id); + expect(name).toEqual(todo.name); + expect(note).toEqual(todo.note); + expect(completed).toEqual(todo.completed); + }); + }); + + it('should return 404', async () => { + await app + .inject({ + method: 'GET', + url: `/v1/todos/${Date.now()}`, + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('GET /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'GET', + url: '/v1/todos/1', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/remove.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/remove.e2e-spec.ts new file mode 100644 index 00000000..ee566afc --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/remove.e2e-spec.ts @@ -0,0 +1,129 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; + +describe('DELETE /v1/todos', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 200 when todo is deleted', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1`, + }) + .then(async ({ statusCode }) => { + expect(statusCode).toBe(200); + const todo = await nestKnexService + .connection('todos') + .where({ id: 1 }) + .first(); + expect(todo).toBeUndefined(); + }); + }); + + it('should return 404 - non existing todo', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1000`, + }) + .then((res) => expectError(new NotFoundException(), res.json)); + }); +}); + +describe('DELETE /v1/todos - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'DELETE', + url: `/v1/todos/1`, + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/update.e2e-spec.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/update.e2e-spec.ts new file mode 100644 index 00000000..af35c1e6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/update.e2e-spec.ts @@ -0,0 +1,162 @@ +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { getTodoPayload } from './utils/get-todo-payload'; +import { AuthGuard } from '@api/auth/guards/auth.guard'; +import { NestKnexService } from '@database/nest-knex.service'; +import { + ValidationPipe, + ExecutionContext, + UnauthorizedException, + NotFoundException, +} from '@nestjs/common'; +import { ServiceToHttpErrorsInterceptor } from '@utils/interceptors'; +import { expectError } from '../utils/expect-error'; + +describe('PUT /v1/todos/:id', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + const canActivate = jest.fn(); + + class AuthGuardMock { + canActivate = canActivate; + } + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(AuthGuard) + .useClass(AuthGuardMock) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + }), + ); + + app.useGlobalInterceptors(new ServiceToHttpErrorsInterceptor()); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + }); + + beforeEach(async () => { + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + + canActivate.mockImplementation((context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + request.user = { sub: 'tz4a98xxat96iws9zmbrgj3a', email: 'hello@email' }; + return true; + }); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return the updated todo', async () => { + await app + .inject({ + method: 'PUT', + url: '/v1/todos/1', + payload: { + name: 'updated', + completed: true, + note: 'updated', + }, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.name).toEqual('updated'); + expect(responseBody.note).toEqual('updated'); + expect(responseBody.completed).toEqual(true); + }); + }); + + it('should return the not updated todo', async () => { + const todo = await nestKnexService + .connection('todos') + .where({ id: 1 }) + .first(); + + await app + .inject({ + method: 'PUT', + url: `/v1/todos/1`, + payload: {}, + }) + .then((res) => { + const responseBody = res.json(); + expect(res.statusCode).toBe(200); + + expect(responseBody.id).toEqual(todo.id); + expect(responseBody.name).toEqual(todo.name); + expect(responseBody.note).toEqual(todo.note); + expect(responseBody.completed).toEqual(todo.completed); + expect(responseBody.userId).toEqual(todo.userId); + }); + }); + + it('should return 404', async () => { + await app + .inject({ + method: 'PUT', + url: `/v1/todos/32131`, + payload: getTodoPayload(), + }) + .then((res) => { + expectError(new NotFoundException(), res.json); + }); + }); +}); + +describe('PUT /v1/todos/:id - real AuthGuard', () => { + let app: NestFastifyApplication; + let nestKnexService: NestKnexService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + + await app.init(); + + nestKnexService = app.get(NestKnexService); + + await nestKnexService.connection.migrate.rollback(); + await nestKnexService.connection.migrate.latest(); + await nestKnexService.connection.seed.run(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return 401 error', async () => { + await app + .inject({ + method: 'PUT', + url: '/v1/todos/1', + }) + .then((res) => expectError(new UnauthorizedException(), res.json)); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/get-todo-payload.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/get-todo-payload.ts new file mode 100644 index 00000000..d4923a3d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/get-todo-payload.ts @@ -0,0 +1,9 @@ +import { CreateTodoDto } from '@api/todos/dto/create-todo.dto'; + +export const getTodoPayload: (completed?: boolean) => CreateTodoDto = ( + completed, +) => ({ + name: 'Laundry' + Date.now(), + note: 'Buy detergent' + Date.now(), + completed: completed ?? false, +}); diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/index.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/index.ts new file mode 100644 index 00000000..82299d0e --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/index.ts @@ -0,0 +1,2 @@ +export * from './get-todo-payload'; +export * from './sortby-field-todos'; diff --git a/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/sortby-field-todos.ts b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/sortby-field-todos.ts new file mode 100644 index 00000000..7d703492 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/authorization/pg/test/jwt/todos/utils/sortby-field-todos.ts @@ -0,0 +1,17 @@ +import { Todo } from '@api/todos/entities/todo.entity'; +import { SortOrder } from '@utils/query'; + +type ListTodoSortColumn = 'name' | 'createdAt'; + +export const sortByField: ( + list: Todo[], + field: ListTodoSortColumn, + order?: SortOrder, +) => Todo[] = (list, field, order) => { + return list.sort((a, b) => { + if (order === 'desc') { + return b[field].localeCompare(a[field]); + } + return a[field].localeCompare(b[field]); + }); +}; diff --git a/node-cli/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.spec.ts b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.spec.ts new file mode 100644 index 00000000..328dd456 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.spec.ts @@ -0,0 +1,276 @@ +import { authConfig, dbConfig, nodeConfig } from './environment'; + +describe('Environment', () => { + let origEnv: any; + + beforeAll(() => { + origEnv = { ...process.env }; + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + afterEach(() => { + process.env = origEnv; + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('NodeEnvironmentValidator', () => { + function setValidEnv() { + process.env.PORT = '3000'; + process.env.ERROR_LOGGING = 'false'; + process.env.REQUEST_LOGGING = 'false'; + process.env.SWAGGER = 'false'; + process.env.NODE_ENV = 'test'; + } + describe('PORT', () => { + it('transforms the PORT value to a number', () => { + setValidEnv(); + process.env.PORT = '1234'; + + expect(nodeConfig().PORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when PORT is bellow 1024', () => { + setValidEnv(); + process.env.PORT = '1000'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be less than 1024\n', + ); + }); + + it('throws an error when PORT is above 65535', () => { + setValidEnv(); + process.env.PORT = '66666'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be greater than 65535\n', + ); + }); + }); + + describe('NODE_ENV', () => { + it('accepts a valid NODE_ENV', () => { + setValidEnv(); + process.env.NODE_ENV = 'development'; + + expect(nodeConfig().NODE_ENV).toEqual('development'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when NODE_ENV is not valid', () => { + setValidEnv(); + process.env.NODE_ENV = 'test-in-prod'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'NODE_ENV must be one of the following values: development, production, test\n', + ); + }); + }); + + ['SWAGGER', 'REQUEST_LOGGING', 'ERROR_LOGGING'].forEach((env) => { + describe(env, () => { + it(`transforms the ${env} value to a boolean`, () => { + setValidEnv(); + process.env[env] = 'false'; + + expect(nodeConfig()[env as never]).toEqual(true); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('DatabaseEnvironmentValidator', () => { + function setValidEnv() { + process.env.MONGO_PROTOCOL = 'mongodb'; + process.env.MONGO_HOST = 'localhost'; + process.env.MONGO_PORT = '27017'; + process.env.MONGO_USER = 'user'; + process.env.MONGO_PASSWORD = 'password'; + process.env.MONGO_DATABASE_NAME = 'test-db'; + } + describe('MONGO_PROTOCOL', () => { + it('accepts a valid MONGO_PROTOCOL', () => { + setValidEnv(); + process.env.MONGO_PROTOCOL = 'mongodb+srv'; + + expect(dbConfig().MONGO_PROTOCOL).toEqual('mongodb+srv'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when MONGO_PROTOCOL is not valid', () => { + setValidEnv(); + process.env.MONGO_PROTOCOL = 'mongodb-srv'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'MONGO_PROTOCOL must be one of the following values: mongodb, mongodb+srv\n', + ); + }); + }); + + describe('MONGO_PORT', () => { + it('transforms the MONGO_PORT value to a number', () => { + setValidEnv(); + process.env.MONGO_PORT = '1234'; + + expect(dbConfig().MONGO_PORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when MONGO_PORT is bellow 1024', () => { + setValidEnv(); + process.env.MONGO_PORT = '1000'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'MONGO_PORT must not be less than 1024\n', + ); + }); + + it('throws an error when MONGO_PORT is above 65535', () => { + setValidEnv(); + process.env.MONGO_PORT = '66666'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'MONGO_PORT must not be greater than 65535\n', + ); + }); + }); + + [ + 'MONGO_HOST', + 'MONGO_USER', + 'MONGO_PASSWORD', + 'MONGO_DATABASE_NAME', + ].forEach((env) => { + describe(env, () => { + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('AuthEnvironmentValidator', () => { + function setValidEnv() { + process.env.AUTH0_ISSUER_URL = 'http://mock-auth.com/'; + process.env.AUTH0_CLIENT_ID = 'some-client-id'; + process.env.AUTH0_AUDIENCE = 'some-audience'; + process.env.AUTH0_CLIENT_SECRET = 'some-client-secret'; + } + describe('AUTH0_ISSUER_URL', () => { + it('adds a / at the end of the url if there is none', () => { + setValidEnv(); + process.env.AUTH0_ISSUER_URL = 'http://mock-auth.net'; + + expect(authConfig().AUTH0_ISSUER_URL).toEqual('http://mock-auth.net/'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('does not add a / at the end of the url if there is one', () => { + setValidEnv(); + + expect(authConfig().AUTH0_ISSUER_URL).toEqual('http://mock-auth.com/'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('defaults to `/` if undefined to prevent throwing in the transformation', () => { + setValidEnv(); + process.env.AUTH0_ISSUER_URL = undefined; + + expect(authConfig().AUTH0_ISSUER_URL).toEqual('/'); + }); + + it('throws an error when AUTH0_ISSUER_URL is not a valid URL', () => { + setValidEnv(); + process.env.AUTH0_ISSUER_URL = 'test'; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'AUTH0_ISSUER_URL must be a URL address\n', + ); + }); + }); + + ['AUTH0_CLIENT_ID', 'AUTH0_AUDIENCE', 'AUTH0_CLIENT_SECRET'].forEach( + (env) => { + describe(env, () => { + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }, + ); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.ts b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.ts new file mode 100644 index 00000000..d881a09c --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-auth0/environment.ts @@ -0,0 +1,138 @@ +import { Transform, plainToClass } from 'class-transformer'; +import { + IsEnum, + IsInt, + IsNotEmpty, + IsString, + IsUrl, + Max, + Min, + validateSync, +} from 'class-validator'; +import { registerAs } from '@nestjs/config'; + +export enum NodeEnvironment { + Development = 'development', + Production = 'production', + Test = 'test', +} + +export enum MongoProtocol { + onPremises = 'mongodb', + atlasCloud = 'mongodb+srv', +} + +class NodeEnvironmentValidator { + @IsEnum(NodeEnvironment) + NODE_ENV: NodeEnvironment; + + @Min(1024) + @Max(65535) + @Transform(({ value }) => +value) + PORT: number; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + ERROR_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + REQUEST_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + SWAGGER: boolean; +} + +class DatabaseEnvironmentValidator { + @IsEnum(MongoProtocol) + MONGO_PROTOCOL: string; + + @IsString() + @IsNotEmpty() + MONGO_HOST: string; + + @IsInt() + @Min(1024) + @Max(65535) + MONGO_PORT: number; + + @IsString() + @IsNotEmpty() + MONGO_USER: string; + + @IsString() + @IsNotEmpty() + MONGO_PASSWORD: string; + + @IsString() + @IsNotEmpty() + MONGO_DATABASE_NAME: string; +} + +class AuthEnvironmentValidator { + @IsString() + @IsUrl() + @Transform(({ value = '' }) => (value.endsWith('/') ? value : `${value}/`)) + AUTH0_ISSUER_URL: string; + + @IsString() + @IsNotEmpty() + AUTH0_CLIENT_ID: string; + + @IsString() + @IsNotEmpty() + AUTH0_AUDIENCE: string; + + @IsString() + @IsNotEmpty() + AUTH0_CLIENT_SECRET: string; +} + +const validate = (validatedConfig: T): T => { + const errors = validateSync(validatedConfig, { + skipMissingProperties: false, + whitelist: true, + stopAtFirstError: true, + }); + + let errorMessage = ''; + errors.forEach((error) => { + for (const constraint in error.constraints) { + errorMessage += `${error.constraints[constraint]}\n`; + } + }); + + if (errors.length > 0) { + console.log('\x1b[4m%s\x1b[0m', 'Environment validation errors:'); + console.log('\x1b[31m%s\x1b[0m', errorMessage); + + process.exit(0); + } + + return validatedConfig; +}; + +export const nodeConfig = registerAs('node', () => + validate( + plainToClass(NodeEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const dbConfig = registerAs('dbConfig', () => + validate( + plainToClass(DatabaseEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const authConfig = registerAs('authConfig', () => + validate( + plainToClass(AuthEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.spec.ts b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.spec.ts new file mode 100644 index 00000000..2d78478d --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.spec.ts @@ -0,0 +1,268 @@ +import { authConfig, dbConfig, nodeConfig } from './environment'; + +describe('Environment', () => { + let origEnv: any; + + beforeAll(() => { + origEnv = { ...process.env }; + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + afterEach(() => { + process.env = origEnv; + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('NodeEnvironmentValidator', () => { + function setValidEnv() { + process.env.PORT = '3000'; + process.env.ERROR_LOGGING = 'false'; + process.env.REQUEST_LOGGING = 'false'; + process.env.SWAGGER = 'false'; + process.env.NODE_ENV = 'test'; + } + describe('PORT', () => { + it('transforms the PORT value to a number', () => { + setValidEnv(); + process.env.PORT = '1234'; + + expect(nodeConfig().PORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when PORT is bellow 1024', () => { + setValidEnv(); + process.env.PORT = '1000'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be less than 1024\n', + ); + }); + + it('throws an error when PORT is above 65535', () => { + setValidEnv(); + process.env.PORT = '66666'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be greater than 65535\n', + ); + }); + }); + + describe('NODE_ENV', () => { + it('accepts a valid NODE_ENV', () => { + setValidEnv(); + process.env.NODE_ENV = 'development'; + + expect(nodeConfig().NODE_ENV).toEqual('development'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when NODE_ENV is not valid', () => { + setValidEnv(); + process.env.NODE_ENV = 'test-in-prod'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'NODE_ENV must be one of the following values: development, production, test\n', + ); + }); + }); + + ['SWAGGER', 'REQUEST_LOGGING', 'ERROR_LOGGING'].forEach((env) => { + describe(env, () => { + it(`transforms the ${env} value to a boolean`, () => { + setValidEnv(); + process.env[env] = 'false'; + + expect(nodeConfig()[env as never]).toEqual(true); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('DatabaseEnvironmentValidator', () => { + function setValidEnv() { + process.env.MONGO_PROTOCOL = 'mongodb'; + process.env.MONGO_HOST = 'localhost'; + process.env.MONGO_PORT = '27017'; + process.env.MONGO_USER = 'user'; + process.env.MONGO_PASSWORD = 'password'; + process.env.MONGO_DATABASE_NAME = 'test-db'; + } + describe('MONGO_PROTOCOL', () => { + it('accepts a valid MONGO_PROTOCOL', () => { + setValidEnv(); + process.env.MONGO_PROTOCOL = 'mongodb+srv'; + + expect(dbConfig().MONGO_PROTOCOL).toEqual('mongodb+srv'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when MONGO_PROTOCOL is not valid', () => { + setValidEnv(); + process.env.MONGO_PROTOCOL = 'mongodb-srv'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'MONGO_PROTOCOL must be one of the following values: mongodb, mongodb+srv\n', + ); + }); + }); + + describe('MONGO_PORT', () => { + it('transforms the MONGO_PORT value to a number', () => { + setValidEnv(); + process.env.MONGO_PORT = '1234'; + + expect(dbConfig().MONGO_PORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when MONGO_PORT is bellow 1024', () => { + setValidEnv(); + process.env.MONGO_PORT = '1000'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'MONGO_PORT must not be less than 1024\n', + ); + }); + + it('throws an error when MONGO_PORT is above 65535', () => { + setValidEnv(); + process.env.MONGO_PORT = '66666'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'MONGO_PORT must not be greater than 65535\n', + ); + }); + }); + + [ + 'MONGO_HOST', + 'MONGO_USER', + 'MONGO_PASSWORD', + 'MONGO_DATABASE_NAME', + ].forEach((env) => { + describe(env, () => { + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('AuthEnvironmentValidator', () => { + function setValidEnv() { + process.env.JWT_SECRET = 'some-secret'; + process.env.JWT_EXPIRATION = '3600'; + } + describe('JWT_EXPIRATION', () => { + it('transforms the JWT_EXPIRATION value to a number', () => { + setValidEnv(); + process.env.JWT_EXPIRATION = '1234'; + + expect(authConfig().JWT_EXPIRATION).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when JWT_EXPIRATION is not integer', () => { + setValidEnv(); + process.env.JWT_EXPIRATION = '123,45'; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'JWT_EXPIRATION must be an integer number\n', + ); + }); + + it(`throws an error when JWT_EXPIRATION is empty`, () => { + setValidEnv(); + delete process.env.JWT_EXPIRATION; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `JWT_EXPIRATION should not be empty\n`, + ); + }); + }); + + describe('JWT_SECRET', () => { + it(`throws an error when JWT_SECRET is empty`, () => { + setValidEnv(); + delete process.env.JWT_SECRET; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `JWT_SECRET should not be empty\n`, + ); + }); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.ts b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.ts new file mode 100644 index 00000000..9a773b97 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/mongodb-jwt/environment.ts @@ -0,0 +1,129 @@ +import { Transform, plainToClass } from 'class-transformer'; +import { + IsEnum, + IsInt, + IsNotEmpty, + IsString, + Max, + Min, + validateSync, +} from 'class-validator'; +import { registerAs } from '@nestjs/config'; + +export enum NodeEnvironment { + Development = 'development', + Production = 'production', + Test = 'test', +} + +export enum MongoProtocol { + onPremises = 'mongodb', + atlasCloud = 'mongodb+srv', +} + +class NodeEnvironmentValidator { + @IsEnum(NodeEnvironment) + NODE_ENV: NodeEnvironment; + + @Min(1024) + @Max(65535) + @Transform(({ value }) => +value) + PORT: number; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + ERROR_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + REQUEST_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + SWAGGER: boolean; +} + +class DatabaseEnvironmentValidator { + @IsEnum(MongoProtocol) + MONGO_PROTOCOL: string; + + @IsString() + @IsNotEmpty() + MONGO_HOST: string; + + @IsInt() + @Min(1024) + @Max(65535) + MONGO_PORT: number; + + @IsString() + @IsNotEmpty() + MONGO_USER: string; + + @IsString() + @IsNotEmpty() + MONGO_PASSWORD: string; + + @IsString() + @IsNotEmpty() + MONGO_DATABASE_NAME: string; +} + +class AuthEnvironmentValidator { + @IsString() + @IsNotEmpty() + JWT_SECRET: string; + + @Transform(({ value }) => +value) + @IsInt() + @IsNotEmpty() + JWT_EXPIRATION: number; +} + +const validate = (validatedConfig: T): T => { + const errors = validateSync(validatedConfig, { + skipMissingProperties: false, + whitelist: true, + stopAtFirstError: true, + }); + + let errorMessage = ''; + errors.forEach((error) => { + for (const constraint in error.constraints) { + errorMessage += `${error.constraints[constraint]}\n`; + } + }); + + if (errors.length > 0) { + console.log('\x1b[4m%s\x1b[0m', 'Environment validation errors:'); + console.log('\x1b[31m%s\x1b[0m', errorMessage); + + process.exit(0); + } + + return validatedConfig; +}; + +export const nodeConfig = registerAs('node', () => + validate( + plainToClass(NodeEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const dbConfig = registerAs('dbConfig', () => + validate( + plainToClass(DatabaseEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const authConfig = registerAs('authConfig', () => + validate( + plainToClass(AuthEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/pg-auth0/environment.spec.ts b/node-cli/assets/nest/multiple-choice-features/environment/pg-auth0/environment.spec.ts new file mode 100644 index 00000000..2d9c6493 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/pg-auth0/environment.spec.ts @@ -0,0 +1,259 @@ +import { authConfig, dbConfig, nodeConfig } from './environment'; + +describe('Environment', () => { + let origEnv: any; + + beforeAll(() => { + origEnv = { ...process.env }; + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + afterEach(() => { + process.env = origEnv; + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('NodeEnvironmentValidator', () => { + function setValidEnv() { + process.env.PORT = '3000'; + process.env.ERROR_LOGGING = 'false'; + process.env.REQUEST_LOGGING = 'false'; + process.env.SWAGGER = 'false'; + process.env.NODE_ENV = 'test'; + } + describe('PORT', () => { + it('transforms the PORT value to a number', () => { + setValidEnv(); + process.env.PORT = '1234'; + + expect(nodeConfig().PORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when PORT is bellow 1024', () => { + setValidEnv(); + process.env.PORT = '1000'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be less than 1024\n', + ); + }); + + it('throws an error when PORT is above 65535', () => { + setValidEnv(); + process.env.PORT = '66666'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be greater than 65535\n', + ); + }); + }); + + describe('NODE_ENV', () => { + it('accepts a valid NODE_ENV', () => { + setValidEnv(); + process.env.NODE_ENV = 'development'; + + expect(nodeConfig().NODE_ENV).toEqual('development'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when NODE_ENV is not valid', () => { + setValidEnv(); + process.env.NODE_ENV = 'test-in-prod'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'NODE_ENV must be one of the following values: development, production, test\n', + ); + }); + }); + + ['SWAGGER', 'REQUEST_LOGGING', 'ERROR_LOGGING'].forEach((env) => { + describe(env, () => { + it(`transforms the ${env} value to a boolean`, () => { + setValidEnv(); + process.env[env] = 'false'; + + expect(nodeConfig()[env as never]).toEqual(true); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('DatabaseEnvironmentValidator', () => { + function setValidEnv() { + process.env.PGHOST = 'localhost'; + process.env.PGPORT = '5432'; + process.env.PGUSER = 'user'; + process.env.PGPASSWORD = 'password'; + process.env.PGDATABASE = 'test-db'; + } + describe('PGPORT', () => { + it('transforms the PGPORT value to a number', () => { + setValidEnv(); + process.env.PGPORT = '1234'; + + expect(dbConfig().PGPORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when PGPORT is bellow 1024', () => { + setValidEnv(); + process.env.PGPORT = '1000'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PGPORT must not be less than 1024\n', + ); + }); + + it('throws an error when PGPORT is above 65535', () => { + setValidEnv(); + process.env.PGPORT = '66666'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PGPORT must not be greater than 65535\n', + ); + }); + + it('throws an error when PGPORT is not integer', () => { + setValidEnv(); + process.env.PGPORT = '123,45'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PGPORT must be an integer number\n', + ); + }); + }); + + ['PGHOST', 'PGUSER', 'PGPASSWORD', 'PGDATABASE'].forEach((env) => { + describe(env, () => { + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('AuthEnvironmentValidator', () => { + function setValidEnv() { + process.env.AUTH0_ISSUER_URL = 'http://mock-auth.com/'; + process.env.AUTH0_CLIENT_ID = 'some-client-id'; + process.env.AUTH0_AUDIENCE = 'some-audience'; + process.env.AUTH0_CLIENT_SECRET = 'some-client-secret'; + } + describe('AUTH0_ISSUER_URL', () => { + it('adds a / at the end of the url if there is none', () => { + setValidEnv(); + process.env.AUTH0_ISSUER_URL = 'http://mock-auth.net'; + + expect(authConfig().AUTH0_ISSUER_URL).toEqual('http://mock-auth.net/'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('does not add a / at the end of the url if there is one', () => { + setValidEnv(); + + expect(authConfig().AUTH0_ISSUER_URL).toEqual('http://mock-auth.com/'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('defaults to `/` if undefined to prevent throwing in the transformation', () => { + setValidEnv(); + process.env.AUTH0_ISSUER_URL = undefined; + + expect(authConfig().AUTH0_ISSUER_URL).toEqual('/'); + }); + + it('throws an error when AUTH0_ISSUER_URL is not a valid URL', () => { + setValidEnv(); + process.env.AUTH0_ISSUER_URL = 'test'; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'AUTH0_ISSUER_URL must be a URL address\n', + ); + }); + }); + + ['AUTH0_CLIENT_ID', 'AUTH0_AUDIENCE', 'AUTH0_CLIENT_SECRET'].forEach( + (env) => { + describe(env, () => { + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }, + ); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/pg-auth0/environment.ts b/node-cli/assets/nest/multiple-choice-features/environment/pg-auth0/environment.ts new file mode 100644 index 00000000..331a5997 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/pg-auth0/environment.ts @@ -0,0 +1,130 @@ +import { Transform, plainToClass } from 'class-transformer'; +import { + IsEnum, + IsInt, + IsNotEmpty, + IsString, + IsUrl, + Max, + Min, + validateSync, +} from 'class-validator'; +import { registerAs } from '@nestjs/config'; + +export enum NodeEnvironment { + Development = 'development', + Production = 'production', + Test = 'test', +} + +class NodeEnvironmentValidator { + @IsEnum(NodeEnvironment) + NODE_ENV: NodeEnvironment; + + @Min(1024) + @Max(65535) + @Transform(({ value }) => +value) + PORT: number; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + ERROR_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + REQUEST_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + SWAGGER: boolean; +} + +class DatabaseEnvironmentValidator { + @IsString() + @IsNotEmpty() + PGHOST: string; + + @Min(1024) + @Max(65535) + @IsInt() + PGPORT: number; + + @IsString() + @IsNotEmpty() + PGUSER: string; + + @IsString() + @IsNotEmpty() + PGPASSWORD: string; + + @IsString() + @IsNotEmpty() + PGDATABASE: string; +} + +class AuthEnvironmentValidator { + @IsString() + @IsUrl() + @Transform(({ value = '' }) => (value.endsWith('/') ? value : `${value}/`)) + AUTH0_ISSUER_URL: string; + + @IsString() + @IsNotEmpty() + AUTH0_CLIENT_ID: string; + + @IsString() + @IsNotEmpty() + AUTH0_AUDIENCE: string; + + @IsString() + @IsNotEmpty() + AUTH0_CLIENT_SECRET: string; +} + +const validate = (validatedConfig: T): T => { + const errors = validateSync(validatedConfig, { + skipMissingProperties: false, + whitelist: true, + stopAtFirstError: true, + }); + + let errorMessage = ''; + errors.forEach((error) => { + for (const constraint in error.constraints) { + errorMessage += `${error.constraints[constraint]}\n`; + } + }); + + if (errors.length > 0) { + console.log('\x1b[4m%s\x1b[0m', 'Environment validation errors:'); + console.log('\x1b[31m%s\x1b[0m', errorMessage); + + process.exit(0); + } + + return validatedConfig; +}; + +export const nodeConfig = registerAs('node', () => + validate( + plainToClass(NodeEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const dbConfig = registerAs('dbConfig', () => + validate( + plainToClass(DatabaseEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const authConfig = registerAs('authConfig', () => + validate( + plainToClass(AuthEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/pg-jwt/environment.spec.ts b/node-cli/assets/nest/multiple-choice-features/environment/pg-jwt/environment.spec.ts new file mode 100644 index 00000000..c9459716 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/pg-jwt/environment.spec.ts @@ -0,0 +1,251 @@ +import { authConfig, dbConfig, nodeConfig } from './environment'; + +describe('Environment', () => { + let origEnv: any; + + beforeAll(() => { + origEnv = { ...process.env }; + jest.spyOn(process, 'exit').mockImplementation(() => true as never); + jest.spyOn(console, 'log').mockImplementation(() => true as never); + }); + + afterEach(() => { + process.env = origEnv; + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.spyOn(process, 'exit').mockRestore(); + jest.spyOn(console, 'log').mockRestore(); + }); + + describe('NodeEnvironmentValidator', () => { + function setValidEnv() { + process.env.PORT = '3000'; + process.env.ERROR_LOGGING = 'false'; + process.env.REQUEST_LOGGING = 'false'; + process.env.SWAGGER = 'false'; + process.env.NODE_ENV = 'test'; + } + describe('PORT', () => { + it('transforms the PORT value to a number', () => { + setValidEnv(); + process.env.PORT = '1234'; + + expect(nodeConfig().PORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when PORT is bellow 1024', () => { + setValidEnv(); + process.env.PORT = '1000'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be less than 1024\n', + ); + }); + + it('throws an error when PORT is above 65535', () => { + setValidEnv(); + process.env.PORT = '66666'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PORT must not be greater than 65535\n', + ); + }); + }); + + describe('NODE_ENV', () => { + it('accepts a valid NODE_ENV', () => { + setValidEnv(); + process.env.NODE_ENV = 'development'; + + expect(nodeConfig().NODE_ENV).toEqual('development'); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when NODE_ENV is not valid', () => { + setValidEnv(); + process.env.NODE_ENV = 'test-in-prod'; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'NODE_ENV must be one of the following values: development, production, test\n', + ); + }); + }); + + ['SWAGGER', 'REQUEST_LOGGING', 'ERROR_LOGGING'].forEach((env) => { + describe(env, () => { + it(`transforms the ${env} value to a boolean`, () => { + setValidEnv(); + process.env[env] = 'false'; + + expect(nodeConfig()[env as never]).toEqual(true); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + nodeConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('DatabaseEnvironmentValidator', () => { + function setValidEnv() { + process.env.PGHOST = 'localhost'; + process.env.PGPORT = '5432'; + process.env.PGUSER = 'user'; + process.env.PGPASSWORD = 'password'; + process.env.PGDATABASE = 'test-db'; + } + describe('PGPORT', () => { + it('transforms the PGPORT value to a number', () => { + setValidEnv(); + process.env.PGPORT = '1234'; + + expect(dbConfig().PGPORT).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when PGPORT is bellow 1024', () => { + setValidEnv(); + process.env.PGPORT = '1000'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PGPORT must not be less than 1024\n', + ); + }); + + it('throws an error when PGPORT is above 65535', () => { + setValidEnv(); + process.env.PGPORT = '66666'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PGPORT must not be greater than 65535\n', + ); + }); + + it('throws an error when PGPORT is not integer', () => { + setValidEnv(); + process.env.PGPORT = '123,45'; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'PGPORT must be an integer number\n', + ); + }); + }); + + ['PGHOST', 'PGUSER', 'PGPASSWORD', 'PGDATABASE'].forEach((env) => { + describe(env, () => { + it(`throws an error when ${env} is empty`, () => { + setValidEnv(); + delete process.env[env]; + + dbConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `${env} should not be empty\n`, + ); + }); + }); + }); + }); + + describe('AuthEnvironmentValidator', () => { + function setValidEnv() { + process.env.JWT_SECRET = 'some-secret'; + process.env.JWT_EXPIRATION = '3600'; + } + describe('JWT_EXPIRATION', () => { + it('transforms the JWT_EXPIRATION value to a number', () => { + setValidEnv(); + process.env.JWT_EXPIRATION = '1234'; + + expect(authConfig().JWT_EXPIRATION).toEqual(1234); + expect(process.exit).toHaveBeenCalledTimes(0); + expect(console.log).toHaveBeenCalledTimes(0); + }); + + it('throws an error when JWT_EXPIRATION is not integer', () => { + setValidEnv(); + process.env.JWT_EXPIRATION = '123,45'; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + 'JWT_EXPIRATION must be an integer number\n', + ); + }); + + it(`throws an error when JWT_EXPIRATION is empty`, () => { + setValidEnv(); + delete process.env.JWT_EXPIRATION; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `JWT_EXPIRATION should not be empty\n`, + ); + }); + }); + + describe('JWT_SECRET', () => { + it(`throws an error when JWT_SECRET is empty`, () => { + setValidEnv(); + delete process.env.JWT_SECRET; + + authConfig(); + + expect(process.exit).toHaveBeenCalledWith(0); + expect(console.log).toHaveBeenCalledWith( + '\x1b[31m%s\x1b[0m', + `JWT_SECRET should not be empty\n`, + ); + }); + }); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/environment/pg-jwt/environment.ts b/node-cli/assets/nest/multiple-choice-features/environment/pg-jwt/environment.ts new file mode 100644 index 00000000..e79fe613 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/environment/pg-jwt/environment.ts @@ -0,0 +1,121 @@ +import { Transform, plainToClass } from 'class-transformer'; +import { + IsEnum, + IsInt, + IsNotEmpty, + IsString, + Max, + Min, + validateSync, +} from 'class-validator'; +import { registerAs } from '@nestjs/config'; + +export enum NodeEnvironment { + Development = 'development', + Production = 'production', + Test = 'test', +} + +class NodeEnvironmentValidator { + @IsEnum(NodeEnvironment) + NODE_ENV: NodeEnvironment; + + @Min(1024) + @Max(65535) + @Transform(({ value }) => +value) + PORT: number; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + ERROR_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + REQUEST_LOGGING: boolean; + + @Transform(({ value }) => Boolean(value)) + @IsNotEmpty() + SWAGGER: boolean; +} + +class DatabaseEnvironmentValidator { + @IsString() + @IsNotEmpty() + PGHOST: string; + + @Min(1024) + @Max(65535) + @IsInt() + PGPORT: number; + + @IsString() + @IsNotEmpty() + PGUSER: string; + + @IsString() + @IsNotEmpty() + PGPASSWORD: string; + + @IsString() + @IsNotEmpty() + PGDATABASE: string; +} + +class AuthEnvironmentValidator { + @IsString() + @IsNotEmpty() + JWT_SECRET: string; + + @Transform(({ value }) => +value) + @IsInt() + @IsNotEmpty() + JWT_EXPIRATION: number; +} + +const validate = (validatedConfig: T): T => { + const errors = validateSync(validatedConfig, { + skipMissingProperties: false, + whitelist: true, + stopAtFirstError: true, + }); + + let errorMessage = ''; + errors.forEach((error) => { + for (const constraint in error.constraints) { + errorMessage += `${error.constraints[constraint]}\n`; + } + }); + + if (errors.length > 0) { + console.log('\x1b[4m%s\x1b[0m', 'Environment validation errors:'); + console.log('\x1b[31m%s\x1b[0m', errorMessage); + + process.exit(0); + } + + return validatedConfig; +}; + +export const nodeConfig = registerAs('node', () => + validate( + plainToClass(NodeEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const dbConfig = registerAs('dbConfig', () => + validate( + plainToClass(DatabaseEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); + +export const authConfig = registerAs('authConfig', () => + validate( + plainToClass(AuthEnvironmentValidator, process.env, { + enableImplicitConversion: true, + }), + ), +); diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/index.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/index.ts new file mode 100644 index 00000000..e4aa5074 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/index.ts @@ -0,0 +1 @@ +export * from './user.entity'; diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/user.entity.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/user.entity.ts new file mode 100644 index 00000000..9cbe1034 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/entities/user.entity.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { GenericEntity } from '@utils/entities'; + +export class User extends GenericEntity { + @ApiProperty() + email: string; + + @ApiProperty() + userId: string; +} diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/index.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/index.ts new file mode 100644 index 00000000..9e6f56e6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './user-email-taken.error-mapping'; diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/user-email-taken.error-mapping.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/user-email-taken.error-mapping.ts new file mode 100644 index 00000000..41fe65f5 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/error-mappings/user-email-taken.error-mapping.ts @@ -0,0 +1,3 @@ +import { uniqueViolation } from '@database/error-mappings'; + +export const UserEmailTaken = uniqueViolation('User email already taken'); diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/index.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/index.ts new file mode 100644 index 00000000..96798590 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/index.ts @@ -0,0 +1 @@ +export * from './users.repository'; diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.spec.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.spec.ts new file mode 100644 index 00000000..6e579604 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.spec.ts @@ -0,0 +1,105 @@ +import { UsersRepository } from './users.repository'; +import { Test } from '@nestjs/testing'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; + +describe('UsersRepository', () => { + let usersRepository: UsersRepository; + + const mockFn = jest.fn().mockImplementation(() => Promise.resolve([])); + + const insertOne = jest.fn().mockImplementation(async () => mockFn()); + const findOne = jest.fn().mockImplementation(() => mockFn()); + const findOneAndUpdate = jest.fn().mockImplementation(() => mockFn()); + const findOneAndDelete = jest.fn().mockImplementation(() => mockFn()); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: DatabaseService, + useFactory: () => ({ + connection: { + collection: () => ({ + insertOne, + findOne, + findOneAndUpdate, + findOneAndDelete, + }), + }, + }), + }, + UsersRepository, + ], + }).compile(); + + usersRepository = moduleRef.get(UsersRepository); + }); + + it('insertOne - create a user', async () => { + const insertUser = { + email: 'user@example.com', + userId: 'auth0-user-id', + }; + + const createdUser = { + _id: new ObjectId(100), + email: 'user@example.com', + userId: 'auth0-user-id', + }; + + mockFn.mockImplementationOnce(() => + Promise.resolve({ insertedId: createdUser._id }), + ); + + const result = await usersRepository.insertOne({ + email: insertUser.email, + userId: insertUser.userId, + }); + + expect(result).toBe(createdUser._id); + expect(insertOne).toHaveBeenCalledWith( + expect.objectContaining({ + email: insertUser.email, + userId: insertUser.userId, + }), + ); + }); + + it('findByEmail - find a user', async () => { + const userFound = { + _id: new ObjectId(100), + email: 'user@example.com', + password: 'password', + }; + + mockFn.mockImplementationOnce(() => Promise.resolve(userFound)); + + const result = await usersRepository.findByEmail('user@example.com'); + + expect(result).toBe(userFound); + expect(findOne).toHaveBeenCalledWith({ email: 'user@example.com' }); + }); + + it('updateOne - modify a user', async () => { + const updatedUser = { + _id: new ObjectId(100), + email: 'user@example.com', + password: 'new-password', + }; + + mockFn.mockImplementationOnce(() => Promise.resolve(updatedUser)); + + const result = await usersRepository.updateOne(updatedUser._id, { + email: updatedUser.email, + }); + + expect(result).toBe(updatedUser); + expect(findOneAndUpdate).toHaveBeenCalledWith( + { _id: updatedUser._id }, + { + email: updatedUser.email, + }, + ); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.ts new file mode 100644 index 00000000..707b9a31 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/repositories/users.repository.ts @@ -0,0 +1,44 @@ +import { rethrowError } from '@utils/error'; +import { User } from '../entities'; +import { UserEmailTaken } from '../error-mappings'; +import { Injectable } from '@nestjs/common'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; +import { DatabaseService } from '@database/database.service'; +import { NullableKeysPartial } from '@utils/interfaces'; +import { ObjectId } from 'mongodb'; + +@Injectable() +export class UsersRepository extends BaseRepository { + constructor(private readonly mongodb: DatabaseService) { + super(mongodb, Tables.Users); + } + + insertOne( + payload: NullableKeysPartial>, + ): Promise { + const _id = new ObjectId(); + const user = { + _id, + ...payload, + createdAt: Date.now(), + updatedAt: Date.now(), + }; + + return this.repository() + .insertOne(user) + .then((value) => value.insertedId) + .catch(rethrowError(UserEmailTaken)); + } + + findByEmail(email: User['email']): Promise | null> { + return this.repository().findOne({ email }); + } + + updateOne( + _id: ObjectId, + payload: Partial>, + ): Promise | null> { + return this.repository().findOneAndUpdate({ _id }, payload); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/users.module.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/users.module.ts new file mode 100644 index 00000000..1ce5ac8b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/auth0/users.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { UsersRepository } from './repositories'; +import { DatabaseModule } from '@database/database.module'; + +@Module({ + imports: [DatabaseModule], + providers: [UsersRepository], + exports: [UsersRepository], +}) +export class UsersModule {} diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/index.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/index.ts new file mode 100644 index 00000000..e4aa5074 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/index.ts @@ -0,0 +1 @@ +export * from './user.entity'; diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/user.entity.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/user.entity.ts new file mode 100644 index 00000000..66e66320 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/entities/user.entity.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { GenericEntity } from '@utils/entities'; +import { ObjectId } from 'mongodb'; + +export class User extends GenericEntity { + @ApiProperty() + email: string; + + @ApiProperty() + password: string; + + @ApiProperty() + userId: ObjectId; +} diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/index.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/index.ts new file mode 100644 index 00000000..9e6f56e6 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/index.ts @@ -0,0 +1 @@ +export * from './user-email-taken.error-mapping'; diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/user-email-taken.error-mapping.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/user-email-taken.error-mapping.ts new file mode 100644 index 00000000..41fe65f5 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/error-mappings/user-email-taken.error-mapping.ts @@ -0,0 +1,3 @@ +import { uniqueViolation } from '@database/error-mappings'; + +export const UserEmailTaken = uniqueViolation('User email already taken'); diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/index.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/index.ts new file mode 100644 index 00000000..96798590 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/index.ts @@ -0,0 +1 @@ +export * from './users.repository'; diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.spec.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.spec.ts new file mode 100644 index 00000000..72bf2b35 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.spec.ts @@ -0,0 +1,108 @@ +import { UsersRepository } from './users.repository'; +import { Test } from '@nestjs/testing'; +import { Credentials } from '@api/auth/interfaces'; +import { DatabaseService } from '@database/database.service'; +import { ObjectId } from 'mongodb'; + +describe('UsersRepository', () => { + let usersRepository: UsersRepository; + + const mockFn = jest.fn().mockImplementation(() => Promise.resolve([])); + + const insertOne = jest.fn().mockImplementation(async () => mockFn()); + const findOne = jest.fn().mockImplementation(() => mockFn()); + const findOneAndUpdate = jest.fn().mockImplementation(() => mockFn()); + const findOneAndDelete = jest.fn().mockImplementation(() => mockFn()); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: DatabaseService, + useFactory: () => ({ + connection: { + collection: () => ({ + insertOne, + findOne, + findOneAndUpdate, + findOneAndDelete, + }), + }, + }), + }, + UsersRepository, + ], + }).compile(); + + usersRepository = moduleRef.get(UsersRepository); + }); + + it('insertOne - create a user', async () => { + const insertUser: Credentials = { + email: 'user@example.com', + password: 'password', + }; + + const createdUser = { + _id: new ObjectId(100), + email: 'user@example.com', + password: 'password', + }; + + mockFn.mockImplementationOnce(() => + Promise.resolve({ insertedId: createdUser._id }), + ); + + const result = await usersRepository.insertOne({ + email: insertUser.email, + password: insertUser.password, + }); + + expect(result).toBe(createdUser._id); + expect(insertOne).toHaveBeenCalledWith( + expect.objectContaining({ + email: insertUser.email, + password: insertUser.password, + }), + ); + }); + + it('findByEmail - find a user', async () => { + const userFound = { + _id: new Object(100), + email: 'user@example.com', + password: 'password', + }; + + mockFn.mockImplementationOnce(() => Promise.resolve(userFound)); + + const result = await usersRepository.findByEmail('user@example.com'); + + expect(result).toBe(userFound); + expect(findOne).toHaveBeenCalledWith({ email: 'user@example.com' }); + }); + + it('updateOne - modify a user', async () => { + const updatedUser = { + _id: new ObjectId(100), + email: 'user@example.com', + password: 'new-password', + }; + + mockFn.mockImplementationOnce(() => Promise.resolve(updatedUser)); + + const result = await usersRepository.updateOne(updatedUser._id, { + email: updatedUser.email, + password: updatedUser.password, + }); + + expect(result).toBe(updatedUser); + expect(findOneAndUpdate).toHaveBeenCalledWith( + { _id: updatedUser._id }, + { + email: updatedUser.email, + password: updatedUser.password, + }, + ); + }); +}); diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.ts new file mode 100644 index 00000000..f5e6d5b8 --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/repositories/users.repository.ts @@ -0,0 +1,45 @@ +import { rethrowError } from '@utils/error'; +import { User } from '../entities'; +import { UserEmailTaken } from '../error-mappings'; +import { Injectable } from '@nestjs/common'; +import { BaseRepository } from '@database/base-repository.repository'; +import { Tables } from '@database/constants'; +import { DatabaseService } from '@database/database.service'; +import { NullableKeysPartial } from '@utils/interfaces'; +import { ObjectId } from 'mongodb'; + +@Injectable() +export class UsersRepository extends BaseRepository { + constructor(private readonly mongodb: DatabaseService) { + super(mongodb, Tables.Users); + } + + insertOne( + payload: NullableKeysPartial>, + ): Promise { + const _id = new ObjectId(); + const user = { + _id, + ...payload, + userId: _id, + createdAt: Date.now(), + updatedAt: Date.now(), + }; + + return this.repository() + .insertOne(user) + .then((value) => value.insertedId) + .catch(rethrowError(UserEmailTaken)); + } + + findByEmail(email: User['email']): Promise | null> { + return this.repository().findOne({ email }); + } + + updateOne( + _id: ObjectId, + payload: Partial>, + ): Promise | null> { + return this.repository().findOneAndUpdate({ _id }, payload); + } +} diff --git a/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/users.module.ts b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/users.module.ts new file mode 100644 index 00000000..1ce5ac8b --- /dev/null +++ b/node-cli/assets/nest/multiple-choice-features/users/mongodb/jwt/users.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { UsersRepository } from './repositories'; +import { DatabaseModule } from '@database/database.module'; + +@Module({ + imports: [DatabaseModule], + providers: [UsersRepository], + exports: [UsersRepository], +}) +export class UsersModule {} diff --git a/assets/nest/ts/.gitignore b/node-cli/assets/nest/ts/.gitignore similarity index 100% rename from assets/nest/ts/.gitignore rename to node-cli/assets/nest/ts/.gitignore diff --git a/assets/nest/ts/jest.config.js b/node-cli/assets/nest/ts/jest.config.js similarity index 100% rename from assets/nest/ts/jest.config.js rename to node-cli/assets/nest/ts/jest.config.js diff --git a/node-cli/assets/nest/ts/nest-cli.json b/node-cli/assets/nest/ts/nest-cli.json new file mode 100644 index 00000000..f9aa683b --- /dev/null +++ b/node-cli/assets/nest/ts/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/assets/nest/ts/src/app.controller.spec.ts b/node-cli/assets/nest/ts/src/app.controller.spec.ts similarity index 100% rename from assets/nest/ts/src/app.controller.spec.ts rename to node-cli/assets/nest/ts/src/app.controller.spec.ts diff --git a/assets/nest/ts/src/app.controller.ts b/node-cli/assets/nest/ts/src/app.controller.ts similarity index 100% rename from assets/nest/ts/src/app.controller.ts rename to node-cli/assets/nest/ts/src/app.controller.ts diff --git a/assets/nest/ts/src/app.module.ts b/node-cli/assets/nest/ts/src/app.module.ts similarity index 100% rename from assets/nest/ts/src/app.module.ts rename to node-cli/assets/nest/ts/src/app.module.ts diff --git a/assets/nest/ts/src/app.service.ts b/node-cli/assets/nest/ts/src/app.service.ts similarity index 100% rename from assets/nest/ts/src/app.service.ts rename to node-cli/assets/nest/ts/src/app.service.ts diff --git a/assets/nest/ts/src/main.ts b/node-cli/assets/nest/ts/src/main.ts similarity index 100% rename from assets/nest/ts/src/main.ts rename to node-cli/assets/nest/ts/src/main.ts diff --git a/assets/nest/ts/test/app.e2e-spec.ts b/node-cli/assets/nest/ts/test/app.e2e-spec.ts similarity index 100% rename from assets/nest/ts/test/app.e2e-spec.ts rename to node-cli/assets/nest/ts/test/app.e2e-spec.ts diff --git a/assets/nest/ts/test/jest-e2e.config.js b/node-cli/assets/nest/ts/test/jest-e2e.config.js similarity index 100% rename from assets/nest/ts/test/jest-e2e.config.js rename to node-cli/assets/nest/ts/test/jest-e2e.config.js diff --git a/assets/nest/ts/tsconfig.build.json b/node-cli/assets/nest/ts/tsconfig.build.json similarity index 100% rename from assets/nest/ts/tsconfig.build.json rename to node-cli/assets/nest/ts/tsconfig.build.json diff --git a/assets/nest/ts/tsconfig.json b/node-cli/assets/nest/ts/tsconfig.json similarity index 100% rename from assets/nest/ts/tsconfig.json rename to node-cli/assets/nest/ts/tsconfig.json diff --git a/node-cli/assets/tsconfig.build.json b/node-cli/assets/tsconfig.build.json new file mode 100644 index 00000000..0feb0e16 --- /dev/null +++ b/node-cli/assets/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": [ + "**/*spec.ts", + "**/*test.ts", + "**/*spec-e2e.ts", + "**/*test-e2e.ts", + "**/__mocks__", + ] +} diff --git a/assets/tsconfig.json b/node-cli/assets/tsconfig.json similarity index 100% rename from assets/tsconfig.json rename to node-cli/assets/tsconfig.json diff --git a/assets/vscode/launch.json b/node-cli/assets/vscode/launch.json similarity index 100% rename from assets/vscode/launch.json rename to node-cli/assets/vscode/launch.json diff --git a/bin/node-cli b/node-cli/bin/node-cli similarity index 100% rename from bin/node-cli rename to node-cli/bin/node-cli diff --git a/docs/features.md b/node-cli/docs/features.md similarity index 100% rename from docs/features.md rename to node-cli/docs/features.md diff --git a/node-cli/generate-project.sh b/node-cli/generate-project.sh new file mode 100644 index 00000000..7067df30 --- /dev/null +++ b/node-cli/generate-project.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Define the paths +current_working_directory=$(pwd) + +path_to_old_folder="$(dirname "$current_working_directory")" +path_to_new_folder="$(dirname "$current_working_directory")" + +# Define the possible values +frameworks=("express" "nest") +databases=("pg" "mongodb") +auths=("auth0" "jwt") + +# Iterate over each framework +for framework in ${frameworks[@]}; do + if [ $framework = "express" ]; then + # If express, only use pg for database + databases=("pg") + else + # If not express, use all databases + databases=("pg" "mongodb") + fi + + # Iterate over each database + for database in ${databases[@]}; do + project_database=$database + + if [ $database = "mongodb" ]; then + # If mongodb, change the name to mongo for the project name + project_database="mongo" + fi + + for auth in ${auths[@]}; do + # Define the project name + project_name="${framework}-${project_database}-${auth}" + + # Delete the old folder + rm -rf "${path_to_old_folder}/${project_name}" + + node bin/node-cli g $project_name -e --database=$database --framework=$framework --auth=$auth --path="${path_to_new_folder}" & + done + done +done + +wait \ No newline at end of file diff --git a/jest.config.js b/node-cli/jest.config.js similarity index 100% rename from jest.config.js rename to node-cli/jest.config.js diff --git a/node-cli/licenses-allowed.js b/node-cli/licenses-allowed.js new file mode 100644 index 00000000..2bee46b1 --- /dev/null +++ b/node-cli/licenses-allowed.js @@ -0,0 +1,11 @@ +const spdxLicenses = require('spdx-license-list'); + +const separator = process.argv[2]; + +const osiApprovedLicenses = Object.entries(spdxLicenses) + .filter(([, value]) => value.osiApproved) + .map(([k]) => k); + +const reviewedLicenses = require('./licenses-reviewed'); + +console.log(osiApprovedLicenses.concat(reviewedLicenses).join(separator)); diff --git a/node-cli/licenses-reviewed.js b/node-cli/licenses-reviewed.js new file mode 100644 index 00000000..e7844022 --- /dev/null +++ b/node-cli/licenses-reviewed.js @@ -0,0 +1,34 @@ +module.exports = [ + /** + * @link https://creativecommons.org/licenses/by/1.0/ + * + * Dependencies: + * spdx-license-ids + * spdx-license-list + */ + 'CC0-1.0', + + /** + * @link https://creativecommons.org/licenses/by/3.0/ + * + * Dependencies: + * spdx-ranges + */ + 'CC-BY-3.0', + + /** + * @link https://creativecommons.org/licenses/by/4.0/ + * + * Dependencies: + * caniuse-lite + */ + 'CC-BY-4.0', + + /** + * @link https://blueoakcouncil.org/license/1.0.0 + * + * Dependencies: + * markdownlint-cli + */ + 'BlueOak-1.0.0', +]; diff --git a/node-cli/package-lock.json b/node-cli/package-lock.json new file mode 100644 index 00000000..959d93cc --- /dev/null +++ b/node-cli/package-lock.json @@ -0,0 +1,17366 @@ +{ + "name": "@mentormate/node-cli", + "version": "3.2.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@mentormate/node-cli", + "version": "3.2.0", + "license": "MIT", + "dependencies": { + "gluegun": "^5.1.2" + }, + "bin": { + "node-cli": "bin/node-cli" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@typescript-eslint/eslint-plugin": "^6.12.0", + "commitlint": "^17.4.3", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-security": "^1.7.1", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "^0.34.0", + "prettier": "^3.1.0", + "sort-package-json": "^2.4.1", + "spdx-license-list": "^6.6.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@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 + }, + "node_modules/@commitlint/cli": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.4.3.tgz", + "integrity": "sha512-IPTS7AZuBHgD0gl24El8HwuDM9zJN9JLa5KmZUQoFD1BQeGGdzAYJOnAr85CeJWpTDok0BGHDL0+4odnH0iTyA==", + "dev": true, + "dependencies": { + "@commitlint/format": "^17.4.0", + "@commitlint/lint": "^17.4.3", + "@commitlint/load": "^17.4.2", + "@commitlint/read": "^17.4.2", + "@commitlint/types": "^17.4.0", + "execa": "^5.0.0", + "lodash.isfunction": "^3.0.9", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/cli/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.4.3.tgz", + "integrity": "sha512-8EsY2iDw74hCk3hIQSg7/E0R8/KtPjnFPZVwmmHxcjhZjkSykmxysefICPDnbI3xgxfov0zwL1WKDHM8zglJdw==", + "dev": true, + "dependencies": { + "conventional-changelog-conventionalcommits": "^5.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.0.tgz", + "integrity": "sha512-Sa/+8KNpDXz4zT4bVbz2fpFjvgkPO6u2V2fP4TKgt6FjmOw2z3eEX859vtfeaTav/ukBw0/0jr+5ZTZp9zCBhA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.4.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@commitlint/config-validator/node_modules/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 + }, + "node_modules/@commitlint/ensure": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.4.0.tgz", + "integrity": "sha512-7oAxt25je0jeQ/E0O/M8L3ADb1Cvweu/5lc/kYF8g/kXatI0wxGE5La52onnAUAWeWlsuvBNar15WcrmDmr5Mw==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.4.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", + "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", + "dev": true, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/format": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.0.tgz", + "integrity": "sha512-Z2bWAU5+f1YZh9W76c84J8iLIWIvvm+mzqogTz0Nsc1x6EHW0Z2gI38g5HAjB0r0I3ZjR15IDEJKhsxyblcyhA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.4.0", + "chalk": "^4.1.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/format/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/format/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@commitlint/format/node_modules/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 + }, + "node_modules/@commitlint/format/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/format/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.4.2.tgz", + "integrity": "sha512-1b2Y2qJ6n7bHG9K6h8S4lBGUl6kc7mMhJN9gy1SQfUZqe92ToDjUTtgNWb6LbzR1X8Cq4SEus4VU8Z/riEa94Q==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.4.0", + "semver": "7.3.8" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@commitlint/lint": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.4.3.tgz", + "integrity": "sha512-GnPsqEYmXIB/MaBhRMzkiDJWyjuLrKad4xoxKO4N6Kc19iqjR4DPc/bl2dxeW9kUmtrAtefOzIEzJAevpA5y2w==", + "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^17.4.2", + "@commitlint/parse": "^17.4.2", + "@commitlint/rules": "^17.4.3", + "@commitlint/types": "^17.4.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/load": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.4.2.tgz", + "integrity": "sha512-Si++F85rJ9t4hw6JcOw1i2h0fdpdFQt0YKwjuK4bk9KhFjyFkRxvR3SB2dPaMs+EwWlDrDBGL+ygip1QD6gmPw==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^17.4.0", + "@commitlint/execute-rule": "^17.4.0", + "@commitlint/resolve-extends": "^17.4.0", + "@commitlint/types": "^17.4.0", + "@types/node": "*", + "chalk": "^4.1.0", + "cosmiconfig": "^8.0.0", + "cosmiconfig-typescript-loader": "^4.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0", + "resolve-from": "^5.0.0", + "ts-node": "^10.8.1", + "typescript": "^4.6.4" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/load/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/load/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/load/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@commitlint/load/node_modules/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 + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@commitlint/load/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/load/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@commitlint/load/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/load/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/message": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", + "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "dev": true, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/parse": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.4.2.tgz", + "integrity": "sha512-DK4EwqhxfXpyCA+UH8TBRIAXAfmmX4q9QRBz/2h9F9sI91yt6mltTrL6TKURMcjUVmgaB80wgS9QybNIyVBIJA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^17.4.0", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.2.2" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/read": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.4.2.tgz", + "integrity": "sha512-hasYOdbhEg+W4hi0InmXHxtD/1favB4WdwyFxs1eOy/DvMw6+2IZBmATgGOlqhahsypk4kChhxjAFJAZ2F+JBg==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^17.4.0", + "@commitlint/types": "^17.4.0", + "fs-extra": "^11.0.0", + "git-raw-commits": "^2.0.0", + "minimist": "^1.2.6" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.0.tgz", + "integrity": "sha512-3JsmwkrCzoK8sO22AzLBvNEvC1Pmdn/65RKXzEtQMy6oYMl0Snrq97a5bQQEFETF0VsvbtUuKttLqqgn99OXRQ==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^17.4.0", + "@commitlint/types": "^17.4.0", + "import-fresh": "^3.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/resolve-extends/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/rules": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.4.3.tgz", + "integrity": "sha512-xHReDfE3Z+O9p1sXeEhPRSk4FifBsC4EbXzvQ4aa0ykQe+n/iZDd4CrFC/Oiv2K9BU4ZnFHak30IbMLa4ks1Rw==", + "dev": true, + "dependencies": { + "@commitlint/ensure": "^17.4.0", + "@commitlint/message": "^17.4.2", + "@commitlint/to-lines": "^17.4.0", + "@commitlint/types": "^17.4.0", + "execa": "^5.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", + "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", + "dev": true, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/top-level": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", + "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/top-level/node_modules/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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.0.tgz", + "integrity": "sha512-2NjAnq5IcxY9kXtUeO2Ac0aPpvkuOmwbH/BxIm36XXK5LtWFObWJWjXOA+kcaABMrthjWu6la+FUpyYFMHRvbA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/@commitlint/types/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/types/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@commitlint/types/node_modules/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 + }, + "node_modules/@commitlint/types/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/types/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.4.2.tgz", + "integrity": "sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/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 + }, + "node_modules/@jest/console/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.4.2.tgz", + "integrity": "sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.4.2", + "@jest/reporters": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.4.2", + "jest-config": "^29.4.2", + "jest-haste-map": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-resolve-dependencies": "^29.4.2", + "jest-runner": "^29.4.2", + "jest-runtime": "^29.4.2", + "jest-snapshot": "^29.4.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "jest-watcher": "^29.4.2", + "micromatch": "^4.0.4", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/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 + }, + "node_modules/@jest/core/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.4.2.tgz", + "integrity": "sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "jest-mock": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.4.2.tgz", + "integrity": "sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ==", + "dev": true, + "dependencies": { + "expect": "^29.4.2", + "jest-snapshot": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.2.tgz", + "integrity": "sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.4.2.tgz", + "integrity": "sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.4.2", + "jest-mock": "^29.4.2", + "jest-util": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.4.2.tgz", + "integrity": "sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.4.2", + "@jest/expect": "^29.4.2", + "@jest/types": "^29.4.2", + "jest-mock": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.4.2.tgz", + "integrity": "sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2", + "jest-worker": "^29.4.2", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/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 + }, + "node_modules/@jest/reporters/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.2.tgz", + "integrity": "sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.2.tgz", + "integrity": "sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.4.2.tgz", + "integrity": "sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz", + "integrity": "sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.4.2", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.4.2.tgz", + "integrity": "sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.4.2", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-util": "^29.4.2", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/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 + }, + "node_modules/@jest/transform/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.2.tgz", + "integrity": "sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/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 + }, + "node_modules/@jest/types/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.21", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", + "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@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, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", + "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@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==" + }, + "node_modules/@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", + "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/type-utils": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", + "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", + "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", + "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", + "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/apisauce": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/apisauce/-/apisauce-2.1.5.tgz", + "integrity": "sha512-bkMlz0ZUnyS8vDigej9UBYo5dne9/bQrkgIiIkGaiDHF6e5OxhYRLJDYu65V/Ox86tmWVwepIntAoTmk4Db0Hg==", + "dependencies": { + "axios": "^0.21.4" + } + }, + "node_modules/app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/babel-jest": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.4.2.tgz", + "integrity": "sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.4.2", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.4.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/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 + }, + "node_modules/babel-jest/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz", + "integrity": "sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz", + "integrity": "sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.4.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/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, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001452", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", + "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/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 + }, + "node_modules/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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/commitlint": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/commitlint/-/commitlint-17.4.3.tgz", + "integrity": "sha512-3MGkngRG3x3KY5uKWxgyKK7WU5apelorn4jeJsu8aCotuaoPXYtZX8Ym7a/ZzB19UUuWADnKWVTWBePvweu3aA==", + "dev": true, + "dependencies": { + "@commitlint/cli": "^17.4.3", + "@commitlint/types": "^17.4.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v14" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", + "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", + "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "ts-node": ">=10", + "typescript": ">=3" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/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 + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-browser/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/default-browser/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.2.tgz", + "integrity": "sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", + "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", + "dependencies": { + "jake": "^10.6.1" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.295", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz", + "integrity": "sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/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==" + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/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==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-security": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", + "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", + "dev": true, + "dependencies": { + "safe-regex": "^2.1.1" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/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 + }, + "node_modules/eslint/node_modules/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.2.tgz", + "integrity": "sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.4.2", + "jest-get-type": "^29.4.2", + "jest-matcher-utils": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/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 + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/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 + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/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, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-jetpack": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz", + "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==", + "dependencies": { + "minimatch": "^3.0.2", + "rimraf": "^2.6.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/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, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-hooks-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.0.0.tgz", + "integrity": "sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==", + "dev": true, + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + } + }, + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gluegun": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/gluegun/-/gluegun-5.1.2.tgz", + "integrity": "sha512-Cwx/8S8Z4YQg07a6AFsaGnnnmd8mN17414NcPS3OoDtZRwxgsvwRNJNg69niD6fDa8oNwslCG0xH7rEpRNNE/g==", + "dependencies": { + "apisauce": "^2.1.5", + "app-module-path": "^2.2.0", + "cli-table3": "0.6.0", + "colors": "1.4.0", + "cosmiconfig": "7.0.1", + "cross-spawn": "7.0.3", + "ejs": "3.1.6", + "enquirer": "2.3.6", + "execa": "5.1.1", + "fs-jetpack": "4.3.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.lowercase": "^4.3.0", + "lodash.lowerfirst": "^4.3.1", + "lodash.pad": "^4.5.1", + "lodash.padend": "^4.6.1", + "lodash.padstart": "^4.6.1", + "lodash.repeat": "^4.1.0", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.trim": "^4.5.1", + "lodash.trimend": "^4.5.1", + "lodash.trimstart": "^4.5.1", + "lodash.uppercase": "^4.3.0", + "lodash.upperfirst": "^4.3.1", + "ora": "4.0.2", + "pluralize": "^8.0.0", + "semver": "7.3.5", + "which": "2.0.2", + "yargs-parser": "^21.0.0" + }, + "bin": { + "gluegun": "bin/gluegun" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/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==" + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.2.tgz", + "integrity": "sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.4.2", + "@jest/types": "^29.4.2", + "import-local": "^3.0.2", + "jest-cli": "^29.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.4.2.tgz", + "integrity": "sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.4.2.tgz", + "integrity": "sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.4.2", + "@jest/expect": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.4.2", + "jest-matcher-utils": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-runtime": "^29.4.2", + "jest-snapshot": "^29.4.2", + "jest-util": "^29.4.2", + "p-limit": "^3.1.0", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/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 + }, + "node_modules/jest-circus/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.4.2.tgz", + "integrity": "sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q==", + "dev": true, + "dependencies": { + "@jest/core": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/types": "^29.4.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.4.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/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 + }, + "node_modules/jest-cli/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.4.2.tgz", + "integrity": "sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.4.2", + "@jest/types": "^29.4.2", + "babel-jest": "^29.4.2", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.4.2", + "jest-environment-node": "^29.4.2", + "jest-get-type": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-runner": "^29.4.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/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 + }, + "node_modules/jest-config/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.2.tgz", + "integrity": "sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.2", + "jest-get-type": "^29.4.2", + "pretty-format": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/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 + }, + "node_modules/jest-diff/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.2.tgz", + "integrity": "sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.4.2.tgz", + "integrity": "sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.2", + "jest-util": "^29.4.2", + "pretty-format": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/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 + }, + "node_modules/jest-each/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.4.2.tgz", + "integrity": "sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.4.2", + "@jest/fake-timers": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "jest-mock": "^29.4.2", + "jest-util": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.2.tgz", + "integrity": "sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.4.2.tgz", + "integrity": "sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.2", + "jest-util": "^29.4.2", + "jest-worker": "^29.4.2", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz", + "integrity": "sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.2", + "pretty-format": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz", + "integrity": "sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.4.2", + "jest-get-type": "^29.4.2", + "pretty-format": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/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 + }, + "node_modules/jest-matcher-utils/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.2.tgz", + "integrity": "sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.4.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/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 + }, + "node_modules/jest-message-util/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.4.2.tgz", + "integrity": "sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "@types/node": "*", + "jest-util": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.2.tgz", + "integrity": "sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.4.2.tgz", + "integrity": "sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz", + "integrity": "sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.4.2", + "jest-snapshot": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/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 + }, + "node_modules/jest-resolve/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.4.2.tgz", + "integrity": "sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.4.2", + "@jest/environment": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.2", + "jest-environment-node": "^29.4.2", + "jest-haste-map": "^29.4.2", + "jest-leak-detector": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-runtime": "^29.4.2", + "jest-util": "^29.4.2", + "jest-watcher": "^29.4.2", + "jest-worker": "^29.4.2", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/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 + }, + "node_modules/jest-runner/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runner/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.4.2.tgz", + "integrity": "sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.4.2", + "@jest/fake-timers": "^29.4.2", + "@jest/globals": "^29.4.2", + "@jest/source-map": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-mock": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-snapshot": "^29.4.2", + "jest-util": "^29.4.2", + "semver": "^7.3.5", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/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 + }, + "node_modules/jest-runtime/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.4.2.tgz", + "integrity": "sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.4.2", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.4.2", + "jest-get-type": "^29.4.2", + "jest-haste-map": "^29.4.2", + "jest-matcher-utils": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2", + "natural-compare": "^1.4.0", + "pretty-format": "^29.4.2", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/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 + }, + "node_modules/jest-snapshot/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.2.tgz", + "integrity": "sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/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 + }, + "node_modules/jest-util/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.4.2.tgz", + "integrity": "sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.4.2", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.2", + "leven": "^3.1.0", + "pretty-format": "^29.4.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/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 + }, + "node_modules/jest-validate/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.4.2.tgz", + "integrity": "sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.4.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/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 + }, + "node_modules/jest-watcher/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.2.tgz", + "integrity": "sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.4.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==" + }, + "node_modules/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 + }, + "node_modules/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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/license-checker": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", + "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "read-installed": "~4.0.3", + "semver": "^5.5.0", + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-satisfies": "^4.0.0", + "treeify": "^1.1.0" + }, + "bin": { + "license-checker": "bin/license-checker" + } + }, + "node_modules/license-checker/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/license-checker/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/lint-staged": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.2.tgz", + "integrity": "sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.3" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.7.tgz", + "integrity": "sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.8.0", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/listr2/node_modules/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 + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==" + }, + "node_modules/lodash.lowercase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz", + "integrity": "sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA==" + }, + "node_modules/lodash.lowerfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz", + "integrity": "sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==" + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==" + }, + "node_modules/lodash.repeat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", + "integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==" + }, + "node_modules/lodash.trim": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", + "integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==" + }, + "node_modules/lodash.trimend": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz", + "integrity": "sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA==" + }, + "node_modules/lodash.trimstart": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz", + "integrity": "sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.uppercase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz", + "integrity": "sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA==" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==" + }, + "node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/node_modules/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 + }, + "node_modules/log-update/node_modules/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==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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 + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/markdownlint": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.28.2.tgz", + "integrity": "sha512-yYaQXoKKPV1zgrFsyAuZPEQoe+JrY9GDag9ObKpk09twx4OCU5lut+0/kZPrQ3W7w82SmgKhd7D8m34aG1unVw==", + "dev": true, + "dependencies": { + "markdown-it": "13.0.1", + "markdownlint-micromark": "0.1.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.34.0.tgz", + "integrity": "sha512-4G9I++VBTZkaye6Yfc/7dU6HQHcyldZEVB+bYyQJLcpJOHKk/q5ZpGqK80oKMIdlxzsA3aWOJLZ4DkoaoUWXbQ==", + "dev": true, + "dependencies": { + "commander": "~10.0.1", + "get-stdin": "~9.0.0", + "glob": "~10.2.2", + "ignore": "~5.2.4", + "js-yaml": "^4.1.0", + "jsonc-parser": "~3.2.0", + "markdownlint": "~0.28.2", + "minimatch": "~9.0.0", + "run-con": "~1.2.11" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/markdownlint-cli/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz", + "integrity": "sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-micromark": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", + "integrity": "sha512-jRxlQg8KpOfM2IbCL9RXM8ZiYWz2rv6DlZAnGv8ASJQpUh6byTBnEsbuMZ6T2/uIgntyf7SKg/mEaEBo1164fQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/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==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/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 + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/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==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", + "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", + "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1", + "minipass": "^5.0.0 || ^6.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", + "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.2.tgz", + "integrity": "sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.2", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-installed": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", + "dev": true, + "dependencies": { + "debuglog": "^1.0.1", + "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" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.2" + } + }, + "node_modules/read-installed/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "dev": true, + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "dependencies": { + "global-dirs": "^0.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz", + "integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-con": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz", + "integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~3.0.0", + "minimist": "^1.2.6", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/run-con/node_modules/ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "dependencies": { + "regexp-tree": "~0.1.1" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/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 + }, + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true + }, + "node_modules/sort-package-json": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.4.1.tgz", + "integrity": "sha512-Nd3rgLBJcZ4iw7tpuOhwBupG6SvUDU0Fy1cZGAMorA2JmDUb+29Dg5phJK9gapa2Ak9d15w/RuMl/viwX+nKwQ==", + "dev": true, + "dependencies": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "bin": { + "sort-package-json": "cli.js" + } + }, + "node_modules/sort-package-json/node_modules/detect-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", + "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/spdx-license-list": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.6.0.tgz", + "integrity": "sha512-vLwdf9AWgdJQmG8cai2HKfkInFsliKaCCOwXmdVonClIhdURTX61KdDOoXC1qcQ7gDaZj+CUTcrMJeAdnCtrKA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true + }, + "node_modules/spdx-satisfies": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", + "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", + "dev": true, + "dependencies": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/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 + }, + "node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/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 + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz", + "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==", + "dev": true, + "engines": { + "node": ">= 14", + "npm": ">= 7" + } + }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true + }, + "@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "dev": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "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.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@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-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@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.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "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 + }, + "@commitlint/cli": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.4.3.tgz", + "integrity": "sha512-IPTS7AZuBHgD0gl24El8HwuDM9zJN9JLa5KmZUQoFD1BQeGGdzAYJOnAr85CeJWpTDok0BGHDL0+4odnH0iTyA==", + "dev": true, + "requires": { + "@commitlint/format": "^17.4.0", + "@commitlint/lint": "^17.4.3", + "@commitlint/load": "^17.4.2", + "@commitlint/read": "^17.4.2", + "@commitlint/types": "^17.4.0", + "execa": "^5.0.0", + "lodash.isfunction": "^3.0.9", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.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 + } + } + }, + "@commitlint/config-conventional": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.4.3.tgz", + "integrity": "sha512-8EsY2iDw74hCk3hIQSg7/E0R8/KtPjnFPZVwmmHxcjhZjkSykmxysefICPDnbI3xgxfov0zwL1WKDHM8zglJdw==", + "dev": true, + "requires": { + "conventional-changelog-conventionalcommits": "^5.0.0" + } + }, + "@commitlint/config-validator": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.0.tgz", + "integrity": "sha512-Sa/+8KNpDXz4zT4bVbz2fpFjvgkPO6u2V2fP4TKgt6FjmOw2z3eEX859vtfeaTav/ukBw0/0jr+5ZTZp9zCBhA==", + "dev": true, + "requires": { + "@commitlint/types": "^17.4.0", + "ajv": "^8.11.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "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 + } + } + }, + "@commitlint/ensure": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.4.0.tgz", + "integrity": "sha512-7oAxt25je0jeQ/E0O/M8L3ADb1Cvweu/5lc/kYF8g/kXatI0wxGE5La52onnAUAWeWlsuvBNar15WcrmDmr5Mw==", + "dev": true, + "requires": { + "@commitlint/types": "^17.4.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + } + }, + "@commitlint/execute-rule": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", + "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", + "dev": true + }, + "@commitlint/format": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.0.tgz", + "integrity": "sha512-Z2bWAU5+f1YZh9W76c84J8iLIWIvvm+mzqogTz0Nsc1x6EHW0Z2gI38g5HAjB0r0I3ZjR15IDEJKhsxyblcyhA==", + "dev": true, + "requires": { + "@commitlint/types": "^17.4.0", + "chalk": "^4.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==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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" + } + } + } + }, + "@commitlint/is-ignored": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.4.2.tgz", + "integrity": "sha512-1b2Y2qJ6n7bHG9K6h8S4lBGUl6kc7mMhJN9gy1SQfUZqe92ToDjUTtgNWb6LbzR1X8Cq4SEus4VU8Z/riEa94Q==", + "dev": true, + "requires": { + "@commitlint/types": "^17.4.0", + "semver": "7.3.8" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@commitlint/lint": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.4.3.tgz", + "integrity": "sha512-GnPsqEYmXIB/MaBhRMzkiDJWyjuLrKad4xoxKO4N6Kc19iqjR4DPc/bl2dxeW9kUmtrAtefOzIEzJAevpA5y2w==", + "dev": true, + "requires": { + "@commitlint/is-ignored": "^17.4.2", + "@commitlint/parse": "^17.4.2", + "@commitlint/rules": "^17.4.3", + "@commitlint/types": "^17.4.0" + } + }, + "@commitlint/load": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.4.2.tgz", + "integrity": "sha512-Si++F85rJ9t4hw6JcOw1i2h0fdpdFQt0YKwjuK4bk9KhFjyFkRxvR3SB2dPaMs+EwWlDrDBGL+ygip1QD6gmPw==", + "dev": true, + "requires": { + "@commitlint/config-validator": "^17.4.0", + "@commitlint/execute-rule": "^17.4.0", + "@commitlint/resolve-extends": "^17.4.0", + "@commitlint/types": "^17.4.0", + "@types/node": "*", + "chalk": "^4.1.0", + "cosmiconfig": "^8.0.0", + "cosmiconfig-typescript-loader": "^4.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0", + "resolve-from": "^5.0.0", + "ts-node": "^10.8.1", + "typescript": "^4.6.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" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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 + }, + "cosmiconfig": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "dev": true, + "requires": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^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 + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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 + }, + "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" + } + } + } + }, + "@commitlint/message": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", + "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "dev": true + }, + "@commitlint/parse": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.4.2.tgz", + "integrity": "sha512-DK4EwqhxfXpyCA+UH8TBRIAXAfmmX4q9QRBz/2h9F9sI91yt6mltTrL6TKURMcjUVmgaB80wgS9QybNIyVBIJA==", + "dev": true, + "requires": { + "@commitlint/types": "^17.4.0", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.2.2" + } + }, + "@commitlint/read": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.4.2.tgz", + "integrity": "sha512-hasYOdbhEg+W4hi0InmXHxtD/1favB4WdwyFxs1eOy/DvMw6+2IZBmATgGOlqhahsypk4kChhxjAFJAZ2F+JBg==", + "dev": true, + "requires": { + "@commitlint/top-level": "^17.4.0", + "@commitlint/types": "^17.4.0", + "fs-extra": "^11.0.0", + "git-raw-commits": "^2.0.0", + "minimist": "^1.2.6" + } + }, + "@commitlint/resolve-extends": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.0.tgz", + "integrity": "sha512-3JsmwkrCzoK8sO22AzLBvNEvC1Pmdn/65RKXzEtQMy6oYMl0Snrq97a5bQQEFETF0VsvbtUuKttLqqgn99OXRQ==", + "dev": true, + "requires": { + "@commitlint/config-validator": "^17.4.0", + "@commitlint/types": "^17.4.0", + "import-fresh": "^3.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0", + "resolve-global": "^1.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 + } + } + }, + "@commitlint/rules": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.4.3.tgz", + "integrity": "sha512-xHReDfE3Z+O9p1sXeEhPRSk4FifBsC4EbXzvQ4aa0ykQe+n/iZDd4CrFC/Oiv2K9BU4ZnFHak30IbMLa4ks1Rw==", + "dev": true, + "requires": { + "@commitlint/ensure": "^17.4.0", + "@commitlint/message": "^17.4.2", + "@commitlint/to-lines": "^17.4.0", + "@commitlint/types": "^17.4.0", + "execa": "^5.0.0" + } + }, + "@commitlint/to-lines": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", + "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", + "dev": true + }, + "@commitlint/top-level": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", + "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", + "dev": true, + "requires": { + "find-up": "^5.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "@commitlint/types": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.0.tgz", + "integrity": "sha512-2NjAnq5IcxY9kXtUeO2Ac0aPpvkuOmwbH/BxIm36XXK5LtWFObWJWjXOA+kcaABMrthjWu6la+FUpyYFMHRvbA==", + "dev": true, + "requires": { + "chalk": "^4.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==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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" + } + } + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.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": { + "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.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.4.2.tgz", + "integrity": "sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2", + "slash": "^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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/core": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.4.2.tgz", + "integrity": "sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ==", + "dev": true, + "requires": { + "@jest/console": "^29.4.2", + "@jest/reporters": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.4.2", + "jest-config": "^29.4.2", + "jest-haste-map": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-resolve-dependencies": "^29.4.2", + "jest-runner": "^29.4.2", + "jest-runtime": "^29.4.2", + "jest-snapshot": "^29.4.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "jest-watcher": "^29.4.2", + "micromatch": "^4.0.4", + "pretty-format": "^29.4.2", + "slash": "^3.0.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==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/environment": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.4.2.tgz", + "integrity": "sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "jest-mock": "^29.4.2" + } + }, + "@jest/expect": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.4.2.tgz", + "integrity": "sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ==", + "dev": true, + "requires": { + "expect": "^29.4.2", + "jest-snapshot": "^29.4.2" + } + }, + "@jest/expect-utils": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.2.tgz", + "integrity": "sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA==", + "dev": true, + "requires": { + "jest-get-type": "^29.4.2" + } + }, + "@jest/fake-timers": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.4.2.tgz", + "integrity": "sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.4.2", + "jest-mock": "^29.4.2", + "jest-util": "^29.4.2" + } + }, + "@jest/globals": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.4.2.tgz", + "integrity": "sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.2", + "@jest/expect": "^29.4.2", + "@jest/types": "^29.4.2", + "jest-mock": "^29.4.2" + } + }, + "@jest/reporters": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.4.2.tgz", + "integrity": "sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2", + "jest-worker": "^29.4.2", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/schemas": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.2.tgz", + "integrity": "sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.25.16" + } + }, + "@jest/source-map": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.2.tgz", + "integrity": "sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.4.2.tgz", + "integrity": "sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA==", + "dev": true, + "requires": { + "@jest/console": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz", + "integrity": "sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg==", + "dev": true, + "requires": { + "@jest/test-result": "^29.4.2", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.4.2.tgz", + "integrity": "sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.4.2", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-util": "^29.4.2", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/types": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.2.tgz", + "integrity": "sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + } + }, + "@sinclair/typebox": { + "version": "0.25.21", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", + "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==", + "dev": true + }, + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "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.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "@types/node": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", + "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "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/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", + "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/type-utils": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", + "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", + "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", + "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", + "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.12.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "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-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "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" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "apisauce": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/apisauce/-/apisauce-2.1.5.tgz", + "integrity": "sha512-bkMlz0ZUnyS8vDigej9UBYo5dne9/bQrkgIiIkGaiDHF6e5OxhYRLJDYu65V/Ox86tmWVwepIntAoTmk4Db0Hg==", + "requires": { + "axios": "^0.21.4" + } + }, + "app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" + }, + "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" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "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==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "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 + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "babel-jest": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.4.2.tgz", + "integrity": "sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ==", + "dev": true, + "requires": { + "@jest/transform": "^29.4.2", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.4.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "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": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz", + "integrity": "sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "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": "29.4.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz", + "integrity": "sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.4.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "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-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001452", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", + "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==", + "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 + }, + "ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==" + }, + "cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + } + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "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 + }, + "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": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true + }, + "commitlint": { + "version": "17.4.3", + "resolved": "https://registry.npmjs.org/commitlint/-/commitlint-17.4.3.tgz", + "integrity": "sha512-3MGkngRG3x3KY5uKWxgyKK7WU5apelorn4jeJsu8aCotuaoPXYtZX8Ym7a/ZzB19UUuWADnKWVTWBePvweu3aA==", + "dev": true, + "requires": { + "@commitlint/cli": "^17.4.3", + "@commitlint/types": "^17.4.0" + } + }, + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-conventionalcommits": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", + "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + }, + "conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "requires": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + } + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + } + } + }, + "cosmiconfig-typescript-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", + "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "dev": true, + "requires": {} + }, + "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 + }, + "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" + } + }, + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "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": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true + } + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "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==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "dev": true + }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "dependencies": { + "execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, + "detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "dev": true + }, + "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 + }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "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": "29.4.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.2.tgz", + "integrity": "sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "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" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ejs": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", + "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", + "requires": { + "jake": "^10.6.1" + } + }, + "electron-to-chromium": { + "version": "1.4.295", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz", + "integrity": "sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw==", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "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==" + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true + }, + "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" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.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" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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 + }, + "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==", + "dev": true + }, + "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" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.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 + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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 + }, + "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-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + } + }, + "eslint-plugin-security": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", + "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", + "dev": true, + "requires": { + "safe-regex": "^2.1.1" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "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" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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 + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "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" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.2.tgz", + "integrity": "sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.4.2", + "jest-get-type": "^29.4.2", + "jest-matcher-utils": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "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": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "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" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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" + } + }, + "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" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true + } + } + }, + "fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, + "fs-jetpack": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz", + "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==", + "requires": { + "minimatch": "^3.0.2", + "rimraf": "^2.6.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "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 + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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 + }, + "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==", + "dev": true + }, + "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-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "git-hooks-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.0.0.tgz", + "integrity": "sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==", + "dev": true + }, + "git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, + "requires": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "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 + }, + "globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "dependencies": { + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "gluegun": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/gluegun/-/gluegun-5.1.2.tgz", + "integrity": "sha512-Cwx/8S8Z4YQg07a6AFsaGnnnmd8mN17414NcPS3OoDtZRwxgsvwRNJNg69niD6fDa8oNwslCG0xH7rEpRNNE/g==", + "requires": { + "apisauce": "^2.1.5", + "app-module-path": "^2.2.0", + "cli-table3": "0.6.0", + "colors": "1.4.0", + "cosmiconfig": "7.0.1", + "cross-spawn": "7.0.3", + "ejs": "3.1.6", + "enquirer": "2.3.6", + "execa": "5.1.1", + "fs-jetpack": "4.3.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.lowercase": "^4.3.0", + "lodash.lowerfirst": "^4.3.1", + "lodash.pad": "^4.5.1", + "lodash.padend": "^4.6.1", + "lodash.padstart": "^4.6.1", + "lodash.repeat": "^4.1.0", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.trim": "^4.5.1", + "lodash.trimend": "^4.5.1", + "lodash.trimstart": "^4.5.1", + "lodash.uppercase": "^4.3.0", + "lodash.upperfirst": "^4.3.1", + "ora": "4.0.2", + "pluralize": "^8.0.0", + "semver": "7.3.5", + "which": "2.0.2", + "yargs-parser": "^21.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "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": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "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 + }, + "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==" + }, + "husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "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": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "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==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "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.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, + "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==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "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, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.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.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jackspeak": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.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==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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" + } + } + } + }, + "jest": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.2.tgz", + "integrity": "sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg==", + "dev": true, + "requires": { + "@jest/core": "^29.4.2", + "@jest/types": "^29.4.2", + "import-local": "^3.0.2", + "jest-cli": "^29.4.2" + } + }, + "jest-changed-files": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.4.2.tgz", + "integrity": "sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "dependencies": { + "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" + } + } + } + }, + "jest-circus": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.4.2.tgz", + "integrity": "sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.2", + "@jest/expect": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.4.2", + "jest-matcher-utils": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-runtime": "^29.4.2", + "jest-snapshot": "^29.4.2", + "jest-util": "^29.4.2", + "p-limit": "^3.1.0", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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 + }, + "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" + } + }, + "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-cli": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.4.2.tgz", + "integrity": "sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q==", + "dev": true, + "requires": { + "@jest/core": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/types": "^29.4.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.4.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "prompts": "^2.0.1", + "yargs": "^17.3.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.4.2.tgz", + "integrity": "sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.4.2", + "@jest/types": "^29.4.2", + "babel-jest": "^29.4.2", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.4.2", + "jest-environment-node": "^29.4.2", + "jest-get-type": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-runner": "^29.4.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-diff": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.2.tgz", + "integrity": "sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.2", + "jest-get-type": "^29.4.2", + "pretty-format": "^29.4.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.2.tgz", + "integrity": "sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.4.2.tgz", + "integrity": "sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.2", + "jest-util": "^29.4.2", + "pretty-format": "^29.4.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-environment-node": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.4.2.tgz", + "integrity": "sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.2", + "@jest/fake-timers": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "jest-mock": "^29.4.2", + "jest-util": "^29.4.2" + } + }, + "jest-get-type": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.2.tgz", + "integrity": "sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg==", + "dev": true + }, + "jest-haste-map": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.4.2.tgz", + "integrity": "sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.2", + "jest-util": "^29.4.2", + "jest-worker": "^29.4.2", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz", + "integrity": "sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA==", + "dev": true, + "requires": { + "jest-get-type": "^29.4.2", + "pretty-format": "^29.4.2" + } + }, + "jest-matcher-utils": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz", + "integrity": "sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.4.2", + "jest-get-type": "^29.4.2", + "pretty-format": "^29.4.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-message-util": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.2.tgz", + "integrity": "sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.4.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-mock": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.4.2.tgz", + "integrity": "sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "@types/node": "*", + "jest-util": "^29.4.2" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.2.tgz", + "integrity": "sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig==", + "dev": true + }, + "jest-resolve": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.4.2.tgz", + "integrity": "sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.4.2", + "jest-validate": "^29.4.2", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-resolve-dependencies": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz", + "integrity": "sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg==", + "dev": true, + "requires": { + "jest-regex-util": "^29.4.2", + "jest-snapshot": "^29.4.2" + } + }, + "jest-runner": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.4.2.tgz", + "integrity": "sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg==", + "dev": true, + "requires": { + "@jest/console": "^29.4.2", + "@jest/environment": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.2", + "jest-environment-node": "^29.4.2", + "jest-haste-map": "^29.4.2", + "jest-leak-detector": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-runtime": "^29.4.2", + "jest-util": "^29.4.2", + "jest-watcher": "^29.4.2", + "jest-worker": "^29.4.2", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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 + }, + "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" + } + }, + "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": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.4.2.tgz", + "integrity": "sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw==", + "dev": true, + "requires": { + "@jest/environment": "^29.4.2", + "@jest/fake-timers": "^29.4.2", + "@jest/globals": "^29.4.2", + "@jest/source-map": "^29.4.2", + "@jest/test-result": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-mock": "^29.4.2", + "jest-regex-util": "^29.4.2", + "jest-resolve": "^29.4.2", + "jest-snapshot": "^29.4.2", + "jest-util": "^29.4.2", + "semver": "^7.3.5", + "slash": "^3.0.0", + "strip-bom": "^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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-snapshot": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.4.2.tgz", + "integrity": "sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.4.2", + "@jest/transform": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.4.2", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.4.2", + "jest-get-type": "^29.4.2", + "jest-haste-map": "^29.4.2", + "jest-matcher-utils": "^29.4.2", + "jest-message-util": "^29.4.2", + "jest-util": "^29.4.2", + "natural-compare": "^1.4.0", + "pretty-format": "^29.4.2", + "semver": "^7.3.5" + }, + "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-util": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.2.tgz", + "integrity": "sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.4.2.tgz", + "integrity": "sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q==", + "dev": true, + "requires": { + "@jest/types": "^29.4.2", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.2", + "leven": "^3.1.0", + "pretty-format": "^29.4.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" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-watcher": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.4.2.tgz", + "integrity": "sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w==", + "dev": true, + "requires": { + "@jest/test-result": "^29.4.2", + "@jest/types": "^29.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.4.2", + "string-length": "^4.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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-worker": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.2.tgz", + "integrity": "sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.4.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.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": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "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-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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "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==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "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==", + "dev": true + }, + "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 + }, + "license-checker": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", + "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "read-installed": "~4.0.3", + "semver": "^5.5.0", + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-satisfies": "^4.0.0", + "treeify": "^1.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "lint-staged": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.2.tgz", + "integrity": "sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.3" + }, + "dependencies": { + "execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "listr2": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.7.tgz", + "integrity": "sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.8.0", + "through": "^2.3.8", + "wrap-ansi": "^7.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" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.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 + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.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" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==" + }, + "lodash.lowercase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz", + "integrity": "sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA==" + }, + "lodash.lowerfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz", + "integrity": "sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w==" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==" + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" + }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==" + }, + "lodash.repeat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", + "integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==" + }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==" + }, + "lodash.trim": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", + "integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==" + }, + "lodash.trimend": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz", + "integrity": "sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA==" + }, + "lodash.trimstart": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz", + "integrity": "sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "lodash.uppercase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz", + "integrity": "sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA==" + }, + "lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==" + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "requires": { + "chalk": "^2.4.2" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.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 + }, + "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==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "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" + } + }, + "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.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + } + } + }, + "markdownlint": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.28.2.tgz", + "integrity": "sha512-yYaQXoKKPV1zgrFsyAuZPEQoe+JrY9GDag9ObKpk09twx4OCU5lut+0/kZPrQ3W7w82SmgKhd7D8m34aG1unVw==", + "dev": true, + "requires": { + "markdown-it": "13.0.1", + "markdownlint-micromark": "0.1.2" + } + }, + "markdownlint-cli": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.34.0.tgz", + "integrity": "sha512-4G9I++VBTZkaye6Yfc/7dU6HQHcyldZEVB+bYyQJLcpJOHKk/q5ZpGqK80oKMIdlxzsA3aWOJLZ4DkoaoUWXbQ==", + "dev": true, + "requires": { + "commander": "~10.0.1", + "get-stdin": "~9.0.0", + "glob": "~10.2.2", + "ignore": "~5.2.4", + "js-yaml": "^4.1.0", + "jsonc-parser": "~3.2.0", + "markdownlint": "~0.28.2", + "minimatch": "~9.0.0", + "run-con": "~1.2.11" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + }, + "glob": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz", + "integrity": "sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "markdownlint-micromark": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", + "integrity": "sha512-jRxlQg8KpOfM2IbCL9RXM8ZiYWz2rv6DlZAnGv8ASJQpUh6byTBnEsbuMZ6T2/uIgntyf7SKg/mEaEBo1164fQ==", + "dev": true + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "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": { + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "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==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "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==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "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==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + }, + "dependencies": { + "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" + } + } + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "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 + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "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==", + "dev": 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": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "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 + }, + "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==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "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" + } + }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, + "ora": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", + "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==", + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" + }, + "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" + } + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.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" + } + }, + "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==", + "dev": true, + "requires": { + "aggregate-error": "^3.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==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "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" + } + }, + "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 + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "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.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-scurry": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", + "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1", + "minipass": "^5.0.0 || ^6.0.2" + }, + "dependencies": { + "lru-cache": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", + "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "dev": true + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" + }, + "prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-format": { + "version": "29.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.2.tgz", + "integrity": "sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.2", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "read-installed": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", + "dev": 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" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "dev": true, + "requires": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "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" + } + }, + "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" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "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 + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "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-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "requires": { + "global-dirs": "^0.1.1" + } + }, + "resolve.exports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz", + "integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "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" + } + }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "run-con": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz", + "integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~3.0.0", + "minimist": "^1.2.6", + "strip-json-comments": "~3.1.1" + }, + "dependencies": { + "ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "dev": true + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "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 + }, + "safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "requires": { + "regexp-tree": "~0.1.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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==" + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "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==", + "dev": true + }, + "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 + } + } + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "dev": true + }, + "sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true + }, + "sort-package-json": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.4.1.tgz", + "integrity": "sha512-Nd3rgLBJcZ4iw7tpuOhwBupG6SvUDU0Fy1cZGAMorA2JmDUb+29Dg5phJK9gapa2Ak9d15w/RuMl/viwX+nKwQ==", + "dev": true, + "requires": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "dependencies": { + "detect-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", + "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", + "dev": true + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "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 + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "requires": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "spdx-license-list": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.6.0.tgz", + "integrity": "sha512-vLwdf9AWgdJQmG8cai2HKfkInFsliKaCCOwXmdVonClIhdURTX61KdDOoXC1qcQ7gDaZj+CUTcrMJeAdnCtrKA==", + "dev": true + }, + "spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true + }, + "spdx-satisfies": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", + "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", + "dev": true, + "requires": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "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 + } + } + }, + "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" + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "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==", + "dev": true, + "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 + }, + "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-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, + "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==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "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": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "requires": {} + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + } + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "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.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true + }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "requires": { + "defaults": "^1.0.3" + } + }, + "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" + } + }, + "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 + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "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==", + "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 + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "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==", + "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 + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz", + "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==", + "dev": true + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + }, + "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/node-cli/package.json b/node-cli/package.json new file mode 100644 index 00000000..c67a03e0 --- /dev/null +++ b/node-cli/package.json @@ -0,0 +1,58 @@ +{ + "name": "@mentormate/node-cli", + "version": "3.2.0", + "description": "Node CLI", + "repository": { + "url": "git+https://github.com/MentorMate/node-project-cli.git" + }, + "license": "MIT", + "bin": { + "node-cli": "bin/node-cli" + }, + "files": [ + "assets", + "src", + "docs", + "bin" + ], + "scripts": { + "format": "prettier \"**/*.{js,md}\" --write --cache --cache-strategy metadata --cache-location .prettiercache", + "license:check": "license-checker --summary --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", + "license:for-review": "license-checker --summary --exclude $(node ./licenses-allowed.js ',')", + "license:summary": "license-checker --summary", + "lint": "eslint \"**/*.js\" --fix --cache", + "test": "jest", + "test:cov": "jest --coverage", + "test:watch": "jest --watch" + }, + "dependencies": { + "gluegun": "^5.1.2" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@typescript-eslint/eslint-plugin": "^6.12.0", + "commitlint": "^17.4.3", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-security": "^1.7.1", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "^0.34.0", + "prettier": "^3.1.0", + "sort-package-json": "^2.4.1", + "spdx-license-list": "^6.6.0" + }, + "bugs": { + "url": "https://github.com/MentorMate/node-project-cli/issues" + }, + "homepage": "https://github.com/MentorMate/node-project-cli#readme", + "main": ".commitlintrc.js", + "directories": { + "doc": "docs" + }, + "keywords": [], + "author": "" +} diff --git a/src/.npmignore b/node-cli/src/.npmignore similarity index 100% rename from src/.npmignore rename to node-cli/src/.npmignore diff --git a/src/cli.js b/node-cli/src/cli.js similarity index 100% rename from src/cli.js rename to node-cli/src/cli.js diff --git a/src/commands/generate.js b/node-cli/src/commands/generate.js similarity index 75% rename from src/commands/generate.js rename to node-cli/src/commands/generate.js index 5c35592c..06d40e58 100644 --- a/src/commands/generate.js +++ b/node-cli/src/commands/generate.js @@ -1,9 +1,10 @@ 'use strict'; -const { getQuestions } = require('../utils/commands/questions'); +const interactiveAppPrompting = require('../utils/commands/interactive-mode-prompting'); +const exampleAppPrompting = require('../utils/commands/example-app-prompting'); const { CommandError } = require('../errors/command.error'); -const exampleAppConfig = require('../utils/commands/example-app.config'); const getFeatures = require('../utils/commands/features'); +const exampleAppConfig = require('../utils/commands/example-app.config'); const command = { name: 'generate', @@ -26,6 +27,21 @@ const command = { alias: 'e', description: 'Generate an example To-Do app', }, + { + name: 'database', + alias: 'db', + description: 'Possible values: pg, mongodb', + }, + { + name: 'auth', + alias: 'a', + description: 'Possible values: jwt, auth0', + }, + { + name: 'path', + alias: 'p', + description: 'Path to generate the project', + } ], }; @@ -51,18 +67,19 @@ module.exports = { return; } - const isPip3Avaialble = !!which('pip3'); - - if (!isPip3Avaialble) { + const isPip3Available = !!which('pip3'); + if (!isPip3Available) { warning( "No `pip3` found on your system, some of the offered functionalities won't be available", ); } - const pwd = strings.trim(cwd()); + const pwd = strings.trim(options.path || cwd()); const isInteractiveMode = !!options.interactive || !!options['i']; const isExampleApp = !!options['example-app'] || !!options['e']; const framework = options['framework'] || options['f']; + const db = options['database'] || options['db']; + const authOption = options['auth'] || options['a']; const projectName = first; if (isInteractiveMode && isExampleApp) { @@ -71,48 +88,59 @@ module.exports = { ); } + if (!projectName) { + throw new CommandError('You must specify a project name'); + } + + if (isExampleApp) { + if (framework && !['express', 'nest'].includes(framework)) { + return error( + `Invalid framework option "${framework}", possible values: express, nest`, + ); + } + + if (db && !['pg', 'mongodb'].includes(db)) { + return error( + `Invalid database option "${db}", possible values: pg, mongodb`, + ); + } + + if (db && framework === 'express' && db !== 'pg') { + return error( + `Invalid database option "${db}" for express, possible values: pg`, + ); + } + + if (authOption && !['jwt', 'auth0'].includes(authOption)) { + return error( + `Invalid auth option "${authOption}", possible values: jwt, auth0`, + ); + } + } + let userInput = { projectName, framework, isExampleApp, + db, + authOption, }; if (isInteractiveMode) { - userInput = await prompt.ask( - getQuestions(userInput, isPip3Avaialble).slice(0, 2), - ); - - userInput = Object.assign( - {}, - userInput, - await prompt.ask(getQuestions(userInput, isPip3Avaialble).slice(2)), - ); + await interactiveAppPrompting(userInput, prompt, isPip3Available); } - if (userInput.isExampleApp) { - userInput = Object.assign( - {}, - userInput, - await prompt.ask(getQuestions(userInput, isPip3Avaialble)[1]), - ); - userInput = Object.assign( - {}, - userInput, - await prompt.ask(getQuestions(userInput, isPip3Avaialble).slice(2, 5)), - ); + if (isExampleApp) { + await exampleAppPrompting(userInput, prompt); Object.assign( userInput, - exampleAppConfig(userInput.framework, isPip3Avaialble), + exampleAppConfig(userInput.framework, isPip3Available), ); } - if (!userInput.projectName) { - throw new CommandError('You must specify a project name'); - } - userInput.framework ||= 'express'; - userInput.features ||= getFeatures(isPip3Avaialble); + userInput.features ||= getFeatures(isPip3Available); userInput.db ||= 'none'; userInput.projectLanguage = userInput.projectLanguage || 'TS'; userInput.appDir = path(pwd, userInput.projectName); @@ -141,20 +169,17 @@ module.exports = { await toolbox.installNest(userInput); } else if (userInput.framework) { await toolbox.initializeNpm(userInput); - await toolbox.installFramework(userInput); - } - - if (userInput.framework === 'express' && userInput.db === 'none') { - // Postgres is the default db for express - userInput.db = 'pg'; + await toolbox.installExpress(userInput); } - stepsOfExecution.push(toolbox.jsLinters(userInput)); - stepsOfExecution.push(toolbox.jestConfig(userInput)); - stepsOfExecution.push(toolbox.auditConfig(userInput)); - stepsOfExecution.push(toolbox.debug(userInput)); - stepsOfExecution.push(toolbox.generateReadme(userInput)); - stepsOfExecution.push(toolbox.editorconfig(userInput)); + stepsOfExecution.push( + toolbox.jsLinters(userInput), + toolbox.jestConfig(userInput), + toolbox.auditConfig(userInput), + toolbox.debug(userInput), + toolbox.generateReadme(userInput), + toolbox.editorconfig(userInput), + ); if (userInput.projectLanguage === 'TS' && userInput.framework !== 'nest') { stepsOfExecution.push(toolbox.setupTs(userInput)); @@ -189,10 +214,7 @@ module.exports = { stepsOfExecution.push(toolbox.setupMongoDB(userInput)); } - if ( - userInput.isExampleApp && - (!userInput.authOption || userInput.authOption === 'jwt') - ) { + if (userInput.isExampleApp && userInput.authOption === 'jwt') { stepsOfExecution.push(toolbox.setupJwt(userInput)); } @@ -224,7 +246,9 @@ module.exports = { await Promise.all(asyncOperations); - const packageJson = JSON.parse(read(`${userInput.appDir}/package.json`)); + const packageJson = JSON.parse( + read(`${userInput.appDir}/package.json`) || '{}', + ); Object.assign(packageJson, { private: true, diff --git a/src/commands/generate.test.js b/node-cli/src/commands/generate.test.js similarity index 100% rename from src/commands/generate.test.js rename to node-cli/src/commands/generate.test.js diff --git a/src/commands/node-cli.js b/node-cli/src/commands/node-cli.js similarity index 100% rename from src/commands/node-cli.js rename to node-cli/src/commands/node-cli.js diff --git a/src/commands/node-cli.test.js b/node-cli/src/commands/node-cli.test.js similarity index 100% rename from src/commands/node-cli.test.js rename to node-cli/src/commands/node-cli.test.js diff --git a/src/commands/setup-dev.js b/node-cli/src/commands/setup-dev.js similarity index 100% rename from src/commands/setup-dev.js rename to node-cli/src/commands/setup-dev.js diff --git a/src/errors/command.error.js b/node-cli/src/errors/command.error.js similarity index 100% rename from src/errors/command.error.js rename to node-cli/src/errors/command.error.js diff --git a/src/extensions/audit-workflow.js b/node-cli/src/extensions/audit-workflow.js similarity index 100% rename from src/extensions/audit-workflow.js rename to node-cli/src/extensions/audit-workflow.js diff --git a/src/extensions/audit-workflow.test.js b/node-cli/src/extensions/audit-workflow.test.js similarity index 100% rename from src/extensions/audit-workflow.test.js rename to node-cli/src/extensions/audit-workflow.test.js diff --git a/src/extensions/command-help.js b/node-cli/src/extensions/command-help.js similarity index 100% rename from src/extensions/command-help.js rename to node-cli/src/extensions/command-help.js diff --git a/src/extensions/create-project-directory.js b/node-cli/src/extensions/create-project-directory.js similarity index 100% rename from src/extensions/create-project-directory.js rename to node-cli/src/extensions/create-project-directory.js diff --git a/src/extensions/create-project-directory.test.js b/node-cli/src/extensions/create-project-directory.test.js similarity index 100% rename from src/extensions/create-project-directory.test.js rename to node-cli/src/extensions/create-project-directory.test.js diff --git a/src/extensions/debug.js b/node-cli/src/extensions/debug.js similarity index 100% rename from src/extensions/debug.js rename to node-cli/src/extensions/debug.js diff --git a/src/extensions/debug.test.js b/node-cli/src/extensions/debug.test.js similarity index 100% rename from src/extensions/debug.test.js rename to node-cli/src/extensions/debug.test.js diff --git a/src/extensions/dockerize-workflow.js b/node-cli/src/extensions/dockerize-workflow.js similarity index 100% rename from src/extensions/dockerize-workflow.js rename to node-cli/src/extensions/dockerize-workflow.js diff --git a/src/extensions/dockerize-workflow.test.js b/node-cli/src/extensions/dockerize-workflow.test.js similarity index 100% rename from src/extensions/dockerize-workflow.test.js rename to node-cli/src/extensions/dockerize-workflow.test.js diff --git a/src/extensions/editorconfig.js b/node-cli/src/extensions/editorconfig.js similarity index 100% rename from src/extensions/editorconfig.js rename to node-cli/src/extensions/editorconfig.js diff --git a/src/extensions/editorconfig.test.js b/node-cli/src/extensions/editorconfig.test.js similarity index 100% rename from src/extensions/editorconfig.test.js rename to node-cli/src/extensions/editorconfig.test.js diff --git a/src/extensions/generate-readme.js b/node-cli/src/extensions/generate-readme.js similarity index 100% rename from src/extensions/generate-readme.js rename to node-cli/src/extensions/generate-readme.js diff --git a/src/extensions/generate-readme.test.js b/node-cli/src/extensions/generate-readme.test.js similarity index 100% rename from src/extensions/generate-readme.test.js rename to node-cli/src/extensions/generate-readme.test.js diff --git a/src/extensions/husky-setup-extension.js b/node-cli/src/extensions/husky-setup-extension.js similarity index 100% rename from src/extensions/husky-setup-extension.js rename to node-cli/src/extensions/husky-setup-extension.js diff --git a/src/extensions/husky-setup-extension.test.js b/node-cli/src/extensions/husky-setup-extension.test.js similarity index 100% rename from src/extensions/husky-setup-extension.test.js rename to node-cli/src/extensions/husky-setup-extension.test.js diff --git a/src/extensions/initialize-git.js b/node-cli/src/extensions/initialize-git.js similarity index 100% rename from src/extensions/initialize-git.js rename to node-cli/src/extensions/initialize-git.js diff --git a/src/extensions/initialize-git.test.js b/node-cli/src/extensions/initialize-git.test.js similarity index 100% rename from src/extensions/initialize-git.test.js rename to node-cli/src/extensions/initialize-git.test.js diff --git a/src/extensions/initialize-npm.js b/node-cli/src/extensions/initialize-npm.js similarity index 100% rename from src/extensions/initialize-npm.js rename to node-cli/src/extensions/initialize-npm.js diff --git a/src/extensions/initialize-npm.test.js b/node-cli/src/extensions/initialize-npm.test.js similarity index 100% rename from src/extensions/initialize-npm.test.js rename to node-cli/src/extensions/initialize-npm.test.js diff --git a/src/extensions/install-framework.js b/node-cli/src/extensions/install-framework.js similarity index 99% rename from src/extensions/install-framework.js rename to node-cli/src/extensions/install-framework.js index 2b67a31c..73bf02d7 100644 --- a/src/extensions/install-framework.js +++ b/node-cli/src/extensions/install-framework.js @@ -1,7 +1,7 @@ 'use strict'; module.exports = (toolbox) => { - toolbox.installFramework = async ({ + toolbox.installExpress = async ({ devSetup, projectLanguage, framework, diff --git a/src/extensions/install-framework.test.js b/node-cli/src/extensions/install-framework.test.js similarity index 100% rename from src/extensions/install-framework.test.js rename to node-cli/src/extensions/install-framework.test.js diff --git a/src/extensions/install-nest.js b/node-cli/src/extensions/install-nest.js similarity index 98% rename from src/extensions/install-nest.js rename to node-cli/src/extensions/install-nest.js index 56815bb2..3986d49d 100644 --- a/src/extensions/install-nest.js +++ b/node-cli/src/extensions/install-nest.js @@ -32,7 +32,7 @@ module.exports = (toolbox) => { if (!devSetup) { await run( - `npx @nestjs/cli@9.4.2 new ${projectName} --directory ${projectName} --strict --skip-git --skip-install --package-manager npm`, + `cd ${appDir} && cd ../ && npx @nestjs/cli@9.4.2 new ${projectName} --directory ${projectName} --strict --skip-git --skip-install --package-manager npm`, ); await Promise.all([ diff --git a/src/extensions/install-nest.test.js b/node-cli/src/extensions/install-nest.test.js similarity index 100% rename from src/extensions/install-nest.test.js rename to node-cli/src/extensions/install-nest.test.js diff --git a/src/extensions/jest-config-and-coverage-wf.js b/node-cli/src/extensions/jest-config-and-coverage-wf.js similarity index 87% rename from src/extensions/jest-config-and-coverage-wf.js rename to node-cli/src/extensions/jest-config-and-coverage-wf.js index 3c2d0b57..1ac1bdf5 100644 --- a/src/extensions/jest-config-and-coverage-wf.js +++ b/node-cli/src/extensions/jest-config-and-coverage-wf.js @@ -52,13 +52,13 @@ module.exports = (toolbox) => { await copyAsync(`${assetsAppDir}/test/`, `${appDir}/test/`); } - if (isExampleApp && db === 'pg') { + if (isExampleApp) { await copyAsync( `${assetsAppDir}/jest.setup.ts`, `${appDir}/jest.setup.ts`, ); - if (framework === 'express') { + if (framework === 'express' && db === 'pg') { await copyAsync(`${assetsAppDir}/__mocks__/`, `${appDir}/__mocks__/`); } } @@ -72,7 +72,7 @@ module.exports = (toolbox) => { Object.assign(pkgJson.scripts, { test: 'jest', 'test:cov': 'jest --coverage', - 'test:e2e': `DOTENV_CONFIG_PATH=.env.test node -r dotenv/config ./node_modules/jest/bin/jest.js --config ./test/jest-e2e.config.js`, + 'test:e2e': 'npx jest --config ./test/jest-e2e.config.js --runInBand', 'test:e2e:cov': 'npm run test:e2e -- --coverage', }); @@ -96,7 +96,7 @@ module.exports = (toolbox) => { Object.assign(pkgJson.scripts, { 'test:e2e': - 'DOTENV_CONFIG_PATH=.env.test node -r dotenv/config ./node_modules/jest/bin/jest.js --config ./test/jest-e2e.config.js --runInBand', + 'npx jest --config ./test/jest-e2e.config.js --runInBand', }); } } diff --git a/src/extensions/jest-config-and-coverage-wf.test.js b/node-cli/src/extensions/jest-config-and-coverage-wf.test.js similarity index 100% rename from src/extensions/jest-config-and-coverage-wf.test.js rename to node-cli/src/extensions/jest-config-and-coverage-wf.test.js diff --git a/src/extensions/js-linters-extension.js b/node-cli/src/extensions/js-linters-extension.js similarity index 100% rename from src/extensions/js-linters-extension.js rename to node-cli/src/extensions/js-linters-extension.js diff --git a/src/extensions/js-linters-extension.test.js b/node-cli/src/extensions/js-linters-extension.test.js similarity index 100% rename from src/extensions/js-linters-extension.test.js rename to node-cli/src/extensions/js-linters-extension.test.js diff --git a/src/extensions/markdown-linter.js b/node-cli/src/extensions/markdown-linter.js similarity index 100% rename from src/extensions/markdown-linter.js rename to node-cli/src/extensions/markdown-linter.js diff --git a/src/extensions/markdown-linter.test.js b/node-cli/src/extensions/markdown-linter.test.js similarity index 100% rename from src/extensions/markdown-linter.test.js rename to node-cli/src/extensions/markdown-linter.test.js diff --git a/src/extensions/os.js b/node-cli/src/extensions/os.js similarity index 100% rename from src/extensions/os.js rename to node-cli/src/extensions/os.js diff --git a/src/extensions/os.test.js b/node-cli/src/extensions/os.test.js similarity index 100% rename from src/extensions/os.test.js rename to node-cli/src/extensions/os.test.js diff --git a/src/extensions/setup-auth0-express.js b/node-cli/src/extensions/setup-auth0-express.js similarity index 100% rename from src/extensions/setup-auth0-express.js rename to node-cli/src/extensions/setup-auth0-express.js diff --git a/src/extensions/setup-auth0-nest.js b/node-cli/src/extensions/setup-auth0-nest.js similarity index 100% rename from src/extensions/setup-auth0-nest.js rename to node-cli/src/extensions/setup-auth0-nest.js diff --git a/src/extensions/setup-jwt.js b/node-cli/src/extensions/setup-jwt.js similarity index 100% rename from src/extensions/setup-jwt.js rename to node-cli/src/extensions/setup-jwt.js diff --git a/src/extensions/setup-jwt.test.js b/node-cli/src/extensions/setup-jwt.test.js similarity index 100% rename from src/extensions/setup-jwt.test.js rename to node-cli/src/extensions/setup-jwt.test.js diff --git a/src/extensions/setup-license-checks.js b/node-cli/src/extensions/setup-license-checks.js similarity index 100% rename from src/extensions/setup-license-checks.js rename to node-cli/src/extensions/setup-license-checks.js diff --git a/src/extensions/setup-license-checks.test.js b/node-cli/src/extensions/setup-license-checks.test.js similarity index 100% rename from src/extensions/setup-license-checks.test.js rename to node-cli/src/extensions/setup-license-checks.test.js diff --git a/src/extensions/setup-mongodb.js b/node-cli/src/extensions/setup-mongodb.js similarity index 100% rename from src/extensions/setup-mongodb.js rename to node-cli/src/extensions/setup-mongodb.js diff --git a/src/extensions/setup-postgresql.js b/node-cli/src/extensions/setup-postgresql.js similarity index 100% rename from src/extensions/setup-postgresql.js rename to node-cli/src/extensions/setup-postgresql.js diff --git a/src/extensions/setup-postgresql.test.js b/node-cli/src/extensions/setup-postgresql.test.js similarity index 100% rename from src/extensions/setup-postgresql.test.js rename to node-cli/src/extensions/setup-postgresql.test.js diff --git a/src/extensions/ts-setup.js b/node-cli/src/extensions/ts-setup.js similarity index 100% rename from src/extensions/ts-setup.js rename to node-cli/src/extensions/ts-setup.js diff --git a/src/extensions/ts-setup.test.js b/node-cli/src/extensions/ts-setup.test.js similarity index 100% rename from src/extensions/ts-setup.test.js rename to node-cli/src/extensions/ts-setup.test.js diff --git a/src/templates/README.md.ejs b/node-cli/src/templates/README.md.ejs similarity index 100% rename from src/templates/README.md.ejs rename to node-cli/src/templates/README.md.ejs diff --git a/src/templates/dotenv/.env.example.ejs b/node-cli/src/templates/dotenv/.env.example.ejs similarity index 100% rename from src/templates/dotenv/.env.example.ejs rename to node-cli/src/templates/dotenv/.env.example.ejs diff --git a/src/templates/eslintrc-model.js.ejs b/node-cli/src/templates/eslintrc-model.js.ejs similarity index 100% rename from src/templates/eslintrc-model.js.ejs rename to node-cli/src/templates/eslintrc-model.js.ejs diff --git a/src/templates/husky/pre-commit.ejs b/node-cli/src/templates/husky/pre-commit.ejs similarity index 100% rename from src/templates/husky/pre-commit.ejs rename to node-cli/src/templates/husky/pre-commit.ejs diff --git a/src/templates/nodemon/nodemon.json.ejs b/node-cli/src/templates/nodemon/nodemon.json.ejs similarity index 100% rename from src/templates/nodemon/nodemon.json.ejs rename to node-cli/src/templates/nodemon/nodemon.json.ejs diff --git a/node-cli/src/utils/commands/example-app-prompting.js b/node-cli/src/utils/commands/example-app-prompting.js new file mode 100644 index 00000000..56bf209f --- /dev/null +++ b/node-cli/src/utils/commands/example-app-prompting.js @@ -0,0 +1,40 @@ +const { + selectFramework, + selectAuthentication, + selectDatabase +} = require('./questions'); + +const pickOptions = async (userInput, prompt) => { + if (!userInput.framework) { + const selectFrameworkPrompt = selectFramework(); + Object.assign( + userInput, + await prompt.ask(selectFrameworkPrompt), + ); + } + + if (!userInput.authOption) { + const selectAuthenticationPrompt = selectAuthentication(); + Object.assign( + userInput, + await prompt.ask(selectAuthenticationPrompt), + ); + } + + if (userInput.framework === 'nest' && !userInput.db) { + const selectDatabasePrompt = selectDatabase(); + Object.assign( + userInput, + await prompt.ask(selectDatabasePrompt), + ) + } + + if (userInput.framework === 'express') { + // Postgres is the default db for express + userInput.db = 'pg'; + } + + return userInput; +} + +module.exports = pickOptions diff --git a/src/utils/commands/example-app.config.js b/node-cli/src/utils/commands/example-app.config.js similarity index 100% rename from src/utils/commands/example-app.config.js rename to node-cli/src/utils/commands/example-app.config.js diff --git a/src/utils/commands/features.js b/node-cli/src/utils/commands/features.js similarity index 100% rename from src/utils/commands/features.js rename to node-cli/src/utils/commands/features.js diff --git a/node-cli/src/utils/commands/interactive-mode-prompting.js b/node-cli/src/utils/commands/interactive-mode-prompting.js new file mode 100644 index 00000000..b96ede81 --- /dev/null +++ b/node-cli/src/utils/commands/interactive-mode-prompting.js @@ -0,0 +1,17 @@ +const { getQuestions } = require('./questions'); + +const pickOptions = async (userInput, prompt, isPip3Available) => { + Object.assign( + userInput, + await prompt.ask( + getQuestions(userInput, isPip3Available).slice(0, 2), + ) + ); + + Object.assign( + userInput, + await prompt.ask(getQuestions(userInput, isPip3Available).slice(2)), + ); +}; + +module.exports = pickOptions; diff --git a/src/utils/commands/questions.js b/node-cli/src/utils/commands/questions.js similarity index 100% rename from src/utils/commands/questions.js rename to node-cli/src/utils/commands/questions.js diff --git a/src/utils/test/mocks.js b/node-cli/src/utils/test/mocks.js similarity index 100% rename from src/utils/test/mocks.js rename to node-cli/src/utils/test/mocks.js diff --git a/package-lock.json b/package-lock.json index 959d93cc..8fbcf0f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3342 +1,3784 @@ { - "name": "@mentormate/node-cli", + "name": "@mentormate/mono-node-cli", "version": "3.2.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@mentormate/node-cli", + "name": "@mentormate/mono-node-cli", "version": "3.2.0", "license": "MIT", - "dependencies": { - "gluegun": "^5.1.2" - }, - "bin": { - "node-cli": "bin/node-cli" + "workspaces": [ + "node-cli", + "nest-pg-auth0", + "nest-pg-jwt", + "nest-mongo-auth0", + "nest-mongo-jwt", + "express-pg-jwt", + "express-pg-auth0" + ] + }, + "express-pg-auth0": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "axios": "^1.6.3", + "bcrypt": "^5.1.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "express-jwt": "^8.4.1", + "express-oauth2-jwt-bearer": "^1.6.0", + "helmet": "^6.0.1", + "http-errors": "^2.0.0", + "http-terminator": "^3.2.0", + "jsonwebtoken": "^9.0.0", + "knex": "^2.4.2", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "pino": "^8.11.0", + "query-types": "^0.1.4", + "statuses": "^2.0.1", + "zod": "^3.20.6", + "zod-openapi": "~2.2.2" }, "devDependencies": { "@commitlint/config-conventional": "^17.4.3", - "@typescript-eslint/eslint-plugin": "^6.12.0", + "@ls-lint/ls-lint": "^1.11.2", + "@tsconfig/recommended": "^1.0.2", + "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.2", + "@types/cors": "^2.8.5", + "@types/express": "^4.17.17", + "@types/http-errors": "^2.0.1", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "^18.14.0", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "commitizen": "^4.3.0", "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", "eslint": "^8.34.0", "eslint-config-prettier": "^8.6.0", - "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-security": "^1.7.1", + "eslint-plugin-security": "~1.7.1", "husky": "^8.0.3", "jest": "^29.4.2", "license-checker": "^25.0.1", "lint-staged": "^13.1.2", - "markdownlint-cli": "^0.34.0", - "prettier": "^3.1.0", + "markdownlint-cli": "~0.34.0", + "nodemon": "^3.0.2", + "pgtools": "^1.0.0", + "pino-pretty": "^9.4.0", + "prettier": "^2.8.4", + "rimraf": "^4.1.2", + "shellcheck": "^2.2.0", "sort-package-json": "^2.4.1", - "spdx-license-list": "^6.6.0" + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "tsc-alias": "^1.8.2", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "express-pg-auth0/node_modules/@types/node": { + "version": "18.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", + "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "undici-types": "~5.26.4" } }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "express-pg-auth0/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/compat-data": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", - "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "express-pg-auth0/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "express-pg-auth0/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/babel" + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "express-pg-auth0/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/generator": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", - "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "express-pg-auth0/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.20.7", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "express-pg-auth0/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=6.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "express-pg-auth0/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "express-pg-auth0/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "express-pg-auth0/node_modules/axios": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "express-pg-auth0/node_modules/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, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, "engines": { - "node": ">=6.9.0" + "node": ">=8.0.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "express-pg-auth0/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=4.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "express-pg-auth0/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "express-pg-auth0/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "node": ">=16 || 14 >=14.17" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "express-pg-auth0/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "express-pg-auth0/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "@babel/types": "^7.20.2" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "express-pg-auth0/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "express-pg-auth0/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=6.9.0" + "node": ">=4.2.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "express-pg-jwt": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "bcrypt": "^5.1.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "express-jwt": "^8.4.1", + "helmet": "^6.0.1", + "http-errors": "^2.0.0", + "http-terminator": "^3.2.0", + "jsonwebtoken": "^9.0.0", + "knex": "^2.4.2", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "pino": "^8.11.0", + "query-types": "^0.1.4", + "statuses": "^2.0.1", + "zod": "^3.20.6", + "zod-openapi": "~2.2.2" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@ls-lint/ls-lint": "^1.11.2", + "@tsconfig/recommended": "^1.0.2", + "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.2", + "@types/cors": "^2.8.5", + "@types/express": "^4.17.17", + "@types/http-errors": "^2.0.1", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "^18.14.0", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-security": "~1.7.1", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "nodemon": "^3.0.2", + "pgtools": "^1.0.0", + "pino-pretty": "^9.4.0", + "prettier": "^2.8.4", + "rimraf": "^4.1.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "tsc-alias": "^1.8.2", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, "engines": { - "node": ">=6.9.0" + "node": ">=18.6.0", + "npm": ">=9.5.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "express-pg-jwt/node_modules/@types/node": { + "version": "18.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", + "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", "dev": true, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "undici-types": "~5.26.4" } }, - "node_modules/@babel/helpers": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", - "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "express-pg-jwt/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "express-pg-jwt/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.20.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", - "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "express-pg-jwt/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "express-pg-jwt/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/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, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=4.0" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "brace-expansion": "^2.0.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@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==", + "express-pg-jwt/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=8" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "express-pg-jwt/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=10.13.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "express-pg-jwt/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=14" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "express-pg-jwt/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=6.9.0" + "node": ">=4.2.0" } }, - "node_modules/@babel/traverse": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", - "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.13", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", - "globals": "^11.1.0" + "nest-mongo-auth0": { + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/axios": "^3.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/passport": "^10.0.1", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "jwks-rsa": "^3.0.1", + "mongodb": "^6.3.0", + "passport-jwt": "^4.0.1", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/node": "18.16.12", + "@types/passport-jwt": "^3.0.9", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.6.0", + "npm": ">=9.5.0" } }, - "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" + "nest-mongo-auth0/node_modules/@nestjs/axios": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz", + "integrity": "sha512-VlOZhAGDmOoFdsmewn8AyClAdGpKXQQaY1+3PGB+g6ceurGIdTxZgRX3VXc1T6Zs60PedWjg3A82TDOB05mrzQ==", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "axios": "^1.3.1", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0 || ^7.0.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "nest-mongo-auth0/node_modules/@types/node": { + "version": "18.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.12.tgz", + "integrity": "sha512-tIRrjbY9C277MOfP8M3zjMIhtMlUJ6YVqkGgLjz+74jVsdf4/UjC6Hku4+1N0BS0qyC0JAS6tJLUk9H6JUKviQ==", "dev": true }, - "node_modules/@commitlint/cli": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.4.3.tgz", - "integrity": "sha512-IPTS7AZuBHgD0gl24El8HwuDM9zJN9JLa5KmZUQoFD1BQeGGdzAYJOnAr85CeJWpTDok0BGHDL0+4odnH0iTyA==", + "nest-mongo-auth0/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { - "@commitlint/format": "^17.4.0", - "@commitlint/lint": "^17.4.3", - "@commitlint/load": "^17.4.2", - "@commitlint/read": "^17.4.2", - "@commitlint/types": "^17.4.0", - "execa": "^5.0.0", - "lodash.isfunction": "^3.0.9", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "commitlint": "cli.js" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=v14" - } - }, - "node_modules/@commitlint/cli/node_modules/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, - "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/config-conventional": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.4.3.tgz", - "integrity": "sha512-8EsY2iDw74hCk3hIQSg7/E0R8/KtPjnFPZVwmmHxcjhZjkSykmxysefICPDnbI3xgxfov0zwL1WKDHM8zglJdw==", + "nest-mongo-auth0/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "conventional-changelog-conventionalcommits": "^5.0.0" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=v14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/config-validator": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.0.tgz", - "integrity": "sha512-Sa/+8KNpDXz4zT4bVbz2fpFjvgkPO6u2V2fP4TKgt6FjmOw2z3eEX859vtfeaTav/ukBw0/0jr+5ZTZp9zCBhA==", + "nest-mongo-auth0/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.0", - "ajv": "^8.11.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=v14" - } - }, - "node_modules/@commitlint/config-validator/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/config-validator/node_modules/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 - }, - "node_modules/@commitlint/ensure": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.4.0.tgz", - "integrity": "sha512-7oAxt25je0jeQ/E0O/M8L3ADb1Cvweu/5lc/kYF8g/kXatI0wxGE5La52onnAUAWeWlsuvBNar15WcrmDmr5Mw==", + "nest-mongo-auth0/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.0", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=v14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/execute-rule": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", - "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", + "nest-mongo-auth0/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { - "node": ">=v14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/format": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.0.tgz", - "integrity": "sha512-Z2bWAU5+f1YZh9W76c84J8iLIWIvvm+mzqogTz0Nsc1x6EHW0Z2gI38g5HAjB0r0I3ZjR15IDEJKhsxyblcyhA==", + "nest-mongo-auth0/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.0", - "chalk": "^4.1.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=v14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/format/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "nest-mongo-auth0/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@commitlint/format/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "nest-mongo-auth0/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/format/node_modules/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, + "nest-mongo-auth0/node_modules/axios": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "peer": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/@commitlint/format/node_modules/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 - }, - "node_modules/@commitlint/format/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "nest-mongo-auth0/node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/@commitlint/format/node_modules/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==", + "nest-mongo-auth0/node_modules/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, "dependencies": { - "has-flag": "^4.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=8" + "node": ">=8.0.0" } }, - "node_modules/@commitlint/is-ignored": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.4.2.tgz", - "integrity": "sha512-1b2Y2qJ6n7bHG9K6h8S4lBGUl6kc7mMhJN9gy1SQfUZqe92ToDjUTtgNWb6LbzR1X8Cq4SEus4VU8Z/riEa94Q==", + "nest-mongo-auth0/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "@commitlint/types": "^17.4.0", - "semver": "7.3.8" - }, "engines": { - "node": ">=v14" + "node": ">=4.0" } }, - "node_modules/@commitlint/is-ignored/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "nest-mongo-auth0/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { - "semver": "bin/semver.js" + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@commitlint/lint": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.4.3.tgz", - "integrity": "sha512-GnPsqEYmXIB/MaBhRMzkiDJWyjuLrKad4xoxKO4N6Kc19iqjR4DPc/bl2dxeW9kUmtrAtefOzIEzJAevpA5y2w==", + "nest-mongo-auth0/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^17.4.2", - "@commitlint/parse": "^17.4.2", - "@commitlint/rules": "^17.4.3", - "@commitlint/types": "^17.4.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "nest-mongo-auth0/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=v14" + "node": ">=4.2.0" } }, - "node_modules/@commitlint/load": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.4.2.tgz", - "integrity": "sha512-Si++F85rJ9t4hw6JcOw1i2h0fdpdFQt0YKwjuK4bk9KhFjyFkRxvR3SB2dPaMs+EwWlDrDBGL+ygip1QD6gmPw==", - "dev": true, - "dependencies": { - "@commitlint/config-validator": "^17.4.0", - "@commitlint/execute-rule": "^17.4.0", - "@commitlint/resolve-extends": "^17.4.0", - "@commitlint/types": "^17.4.0", - "@types/node": "*", - "chalk": "^4.1.0", - "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4" + "nest-mongo-jwt": { + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "express-jwt": "^8.4.1", + "jsonwebtoken": "^9.0.0", + "mongodb": "^6.3.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "18.16.12", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" }, "engines": { - "node": ">=v14" + "node": ">=18.6.0", + "npm": ">=9.5.0" } }, - "node_modules/@commitlint/load/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "nest-mongo-jwt/node_modules/@types/node": { + "version": "18.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.12.tgz", + "integrity": "sha512-tIRrjbY9C277MOfP8M3zjMIhtMlUJ6YVqkGgLjz+74jVsdf4/UjC6Hku4+1N0BS0qyC0JAS6tJLUk9H6JUKviQ==", + "dev": true + }, + "nest-mongo-jwt/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/load/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@commitlint/load/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "nest-mongo-jwt/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/load/node_modules/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==", + "nest-mongo-jwt/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=7.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/load/node_modules/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 - }, - "node_modules/@commitlint/load/node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "nest-mongo-jwt/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/load/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "nest-mongo-jwt/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/load/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "nest-mongo-jwt/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@commitlint/load/node_modules/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, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/load/node_modules/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==", + "nest-mongo-jwt/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@commitlint/message": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", - "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "nest-mongo-jwt/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, "engines": { - "node": ">=v14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/parse": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.4.2.tgz", - "integrity": "sha512-DK4EwqhxfXpyCA+UH8TBRIAXAfmmX4q9QRBz/2h9F9sI91yt6mltTrL6TKURMcjUVmgaB80wgS9QybNIyVBIJA==", + "nest-mongo-jwt/node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.0", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=v14" + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/@commitlint/read": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.4.2.tgz", - "integrity": "sha512-hasYOdbhEg+W4hi0InmXHxtD/1favB4WdwyFxs1eOy/DvMw6+2IZBmATgGOlqhahsypk4kChhxjAFJAZ2F+JBg==", + "nest-mongo-jwt/node_modules/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, "dependencies": { - "@commitlint/top-level": "^17.4.0", - "@commitlint/types": "^17.4.0", - "fs-extra": "^11.0.0", - "git-raw-commits": "^2.0.0", - "minimist": "^1.2.6" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=v14" + "node": ">=8.0.0" } }, - "node_modules/@commitlint/resolve-extends": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.0.tgz", - "integrity": "sha512-3JsmwkrCzoK8sO22AzLBvNEvC1Pmdn/65RKXzEtQMy6oYMl0Snrq97a5bQQEFETF0VsvbtUuKttLqqgn99OXRQ==", + "nest-mongo-jwt/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "@commitlint/config-validator": "^17.4.0", - "@commitlint/types": "^17.4.0", - "import-fresh": "^3.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" - }, "engines": { - "node": ">=v14" + "node": ">=4.0" } }, - "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "nest-mongo-jwt/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, "engines": { - "node": ">=8" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@commitlint/rules": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.4.3.tgz", - "integrity": "sha512-xHReDfE3Z+O9p1sXeEhPRSk4FifBsC4EbXzvQ4aa0ykQe+n/iZDd4CrFC/Oiv2K9BU4ZnFHak30IbMLa4ks1Rw==", + "nest-mongo-jwt/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { - "@commitlint/ensure": "^17.4.0", - "@commitlint/message": "^17.4.2", - "@commitlint/to-lines": "^17.4.0", - "@commitlint/types": "^17.4.0", - "execa": "^5.0.0" - }, - "engines": { - "node": ">=v14" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/@commitlint/to-lines": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", - "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", + "nest-mongo-jwt/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=v14" + "node": ">=4.2.0" } }, - "node_modules/@commitlint/top-level": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", - "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", - "dev": true, - "dependencies": { - "find-up": "^5.0.0" + "nest-pg-auth0": { + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/axios": "^3.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/passport": "^10.0.1", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "jwks-rsa": "^3.0.1", + "knex": "^2.4.2", + "passport-jwt": "^4.0.1", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/node": "18.16.12", + "@types/passport-jwt": "^3.0.9", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" }, "engines": { - "node": ">=v14" + "node": ">=18.6.0", + "npm": ">=9.5.0" } }, - "node_modules/@commitlint/top-level/node_modules/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==", + "nest-pg-auth0/node_modules/@nestjs/axios": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz", + "integrity": "sha512-VlOZhAGDmOoFdsmewn8AyClAdGpKXQQaY1+3PGB+g6ceurGIdTxZgRX3VXc1T6Zs60PedWjg3A82TDOB05mrzQ==", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "axios": "^1.3.1", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, + "nest-pg-auth0/node_modules/@types/node": { + "version": "18.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.12.tgz", + "integrity": "sha512-tIRrjbY9C277MOfP8M3zjMIhtMlUJ6YVqkGgLjz+74jVsdf4/UjC6Hku4+1N0BS0qyC0JAS6tJLUk9H6JUKviQ==", + "dev": true + }, + "nest-pg-auth0/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/top-level/node_modules/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==", + "nest-pg-auth0/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/top-level/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "nest-pg-auth0/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/top-level/node_modules/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==", + "nest-pg-auth0/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/types": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.0.tgz", - "integrity": "sha512-2NjAnq5IcxY9kXtUeO2Ac0aPpvkuOmwbH/BxIm36XXK5LtWFObWJWjXOA+kcaABMrthjWu6la+FUpyYFMHRvbA==", + "nest-pg-auth0/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, - "dependencies": { - "chalk": "^4.1.0" - }, "engines": { - "node": ">=v14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@commitlint/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "nest-pg-auth0/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "nest-pg-auth0/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@commitlint/types/node_modules/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==", + "nest-pg-auth0/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@commitlint/types/node_modules/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 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/@commitlint/types/node_modules/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, - "engines": { - "node": ">=8" + "nest-pg-auth0/node_modules/axios": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/@commitlint/types/node_modules/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==", + "nest-pg-auth0/node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "nest-pg-auth0/node_modules/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, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=12" + "node": ">=8.0.0" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "nest-pg-auth0/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "engines": { + "node": ">=4.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "nest-pg-auth0/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10.13.0" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "nest-pg-auth0/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "nest-pg-auth0/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=4.2.0" + } + }, + "nest-pg-jwt": { + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@fastify/compress": "^6.4.0", + "@fastify/helmet": "^10.1.1", + "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.1", + "@nestjs/core": "^9.0.0", + "@nestjs/platform-fastify": "^9.0.0", + "@nestjs/swagger": "^6.3.0", + "bcrypt": "^5.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "express-jwt": "^8.4.1", + "jsonwebtoken": "^9.0.0", + "knex": "^2.4.2", + "pg": "^8.9.0", + "pg-error-enum": "^0.6.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "statuses": "^2.0.1" }, - "funding": { - "url": "https://opencollective.com/eslint" + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@fastify/static": "^6.10.2", + "@golevelup/ts-jest": "^0.4.0", + "@ls-lint/ls-lint": "^1.11.2", + "@nestjs/cli": "^9.0.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", + "@types/jest": "^29.4.0", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "18.16.12", + "@types/pg": "^8.6.6", + "@types/statuses": "^2.0.1", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "commitizen": "^4.3.0", + "commitlint": "^17.4.3", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.0.3", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "~0.34.0", + "pgtools": "^1.0.0", + "prettier": "^2.3.2", + "shellcheck": "^2.2.0", + "sort-package-json": "^2.4.1", + "source-map-support": "^0.5.20", + "spdx-license-list": "^6.6.0", + "supertest": "^6.3.3", + "ts-jest": "^29.0.5", + "ts-loader": "^9.2.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.6.0", + "npm": ">=9.5.0" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "nest-pg-jwt/node_modules/@types/node": { + "version": "18.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.12.tgz", + "integrity": "sha512-tIRrjbY9C277MOfP8M3zjMIhtMlUJ6YVqkGgLjz+74jVsdf4/UjC6Hku4+1N0BS0qyC0JAS6tJLUk9H6JUKviQ==", "dev": true }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "nest-pg-jwt/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "nest-pg-jwt/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "nest-pg-jwt/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "nest-pg-jwt/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "nest-pg-jwt/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "nest-pg-jwt/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "nest-pg-jwt/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "nest-pg-jwt/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "nest-pg-jwt/node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@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, - "dependencies": { - "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" + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/@jest/console": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.4.2.tgz", - "integrity": "sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg==", + "nest-pg-jwt/node_modules/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, "dependencies": { - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2", - "slash": "^3.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8.0.0" } }, - "node_modules/@jest/console/node_modules/ansi-styles": { + "nest-pg-jwt/node_modules/estraverse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4.0" } }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "nest-pg-jwt/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@jest/console/node_modules/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==", + "nest-pg-jwt/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "nest-pg-jwt/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=7.0.0" + "node": ">=4.2.0" } }, - "node_modules/@jest/console/node_modules/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 - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/@jest/console/node_modules/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==", + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/@jest/core": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.4.2.tgz", - "integrity": "sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ==", + "node_modules/@angular-devkit/core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.1.tgz", + "integrity": "sha512-2uz98IqkKJlgnHbWQ7VeL4pb+snGAZXIama2KXi+k9GsRntdcw+udX8rL3G9SdUGUF+m6+147Y1oRBMHsO/v4w==", "dev": true, "dependencies": { - "@jest/console": "^29.4.2", - "@jest/reporters": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.4.2", - "jest-config": "^29.4.2", - "jest-haste-map": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-resolve-dependencies": "^29.4.2", - "jest-runner": "^29.4.2", - "jest-runtime": "^29.4.2", - "jest-snapshot": "^29.4.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "jest-watcher": "^29.4.2", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.0", + "rxjs": "7.8.1", + "source-map": "0.7.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "chokidar": "^3.5.2" }, "peerDependenciesMeta": { - "node-notifier": { + "chokidar": { "optional": true } } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@angular-devkit/core/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 8" } }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@angular-devkit/schematics": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.0.1.tgz", + "integrity": "sha512-A9D0LTYmiqiBa90GKcSuWb7hUouGIbm/AHbJbjL85WLLRbQA2PwKl7P5Mpd6nS/ZC0kfG4VQY3VOaDvb3qpI9g==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@angular-devkit/core": "16.0.1", + "jsonc-parser": "3.2.0", + "magic-string": "0.30.0", + "ora": "5.4.1", + "rxjs": "7.8.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@jest/core/node_modules/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==", + "node_modules/@angular-devkit/schematics-cli": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-16.0.1.tgz", + "integrity": "sha512-6KLA125dpgd6oJGtiO2JpZAb92uOG3njQGIt7NFcuQGW/5GO7J41vMXH9cBAfdtbV8SIggSmR/cIEE9ijfj6YQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", + "ansi-colors": "4.1.3", + "inquirer": "8.2.4", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" }, "engines": { - "node": ">=7.0.0" + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@jest/core/node_modules/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 - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@angular-devkit/schematics-cli/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/@jest/core/node_modules/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==", + "node_modules/@angular-devkit/schematics-cli/node_modules/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, "dependencies": { - "has-flag": "^4.0.0" + "restore-cursor": "^3.1.0" }, "engines": { "node": ">=8" } }, - "node_modules/@jest/environment": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.4.2.tgz", - "integrity": "sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ==", + "node_modules/@angular-devkit/schematics-cli/node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "jest-mock": "^29.4.2" + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12.0.0" } }, - "node_modules/@jest/expect": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.4.2.tgz", - "integrity": "sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ==", + "node_modules/@angular-devkit/schematics-cli/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "expect": "^29.4.2", - "jest-snapshot": "^29.4.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/expect-utils": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.2.tgz", - "integrity": "sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA==", + "node_modules/@angular-devkit/schematics-cli/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.2" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/fake-timers": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.4.2.tgz", - "integrity": "sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ==", + "node_modules/@angular-devkit/schematics-cli/node_modules/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, "dependencies": { - "@jest/types": "^29.4.2", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.4.2", - "jest-mock": "^29.4.2", - "jest-util": "^29.4.2" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@jest/globals": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.4.2.tgz", - "integrity": "sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg==", + "node_modules/@angular-devkit/schematics-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "@jest/environment": "^29.4.2", - "@jest/expect": "^29.4.2", - "@jest/types": "^29.4.2", - "jest-mock": "^29.4.2" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jest/reporters": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.4.2.tgz", - "integrity": "sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw==", + "node_modules/@angular-devkit/schematics/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2", - "jest-worker": "^29.4.2", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@angular-devkit/schematics/node_modules/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, "dependencies": { - "color-convert": "^2.0.1" + "restore-cursor": "^3.1.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@angular-devkit/schematics/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/reporters/node_modules/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==", + "node_modules/@angular-devkit/schematics/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/reporters/node_modules/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 - }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@angular-devkit/schematics/node_modules/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, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { "node": ">=8" } }, - "node_modules/@jest/reporters/node_modules/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, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dependencies": { - "has-flag": "^4.0.0" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@jest/schemas": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.2.tgz", - "integrity": "sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g==", - "dev": true, + "node_modules/@babel/code-frame/node_modules/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==", "dependencies": { - "@sinclair/typebox": "^0.25.16" + "color-convert": "^1.9.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/@jest/source-map": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.2.tgz", - "integrity": "sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q==", - "dev": true, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/@jest/test-result": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.4.2.tgz", - "integrity": "sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA==", - "dev": true, + "node_modules/@babel/code-frame/node_modules/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==", "dependencies": { - "@jest/console": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.8.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz", - "integrity": "sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.4.2", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "slash": "^3.0.0" - }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/@jest/transform": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.4.2.tgz", - "integrity": "sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ==", - "dev": true, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.4.2", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-util": "^29.4.2", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "has-flag": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=6.9.0" } }, - "node_modules/@jest/transform/node_modules/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==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jest/transform/node_modules/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 - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "engines": { - "node": ">=8" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@jest/transform/node_modules/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==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@jest/types": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.2.tgz", - "integrity": "sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==", + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6.9.0" } }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/types": "^7.22.15" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=6.9.0" } }, - "node_modules/@jest/types/node_modules/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==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { - "node": ">=7.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@jest/types/node_modules/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 - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@jest/types/node_modules/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==", + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@babel/helpers": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", + "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, + "node_modules/@babel/highlight/node_modules/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==", + "dependencies": { + "color-convert": "^1.9.0" + }, "engines": { - "node": ">=14" + "node": ">=4" } }, - "node_modules/@pkgr/utils": { + "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dev": true, + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.25.21", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", - "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" + "node": ">=4" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", - "dev": true, + "node_modules/@babel/highlight/node_modules/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==", "dependencies": { - "@sinonjs/commons": "^2.0.0" + "color-name": "1.1.3" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "@babel/types": "^7.0.0" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "node_modules/@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, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "node_modules/@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, "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@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==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "node_modules/@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, "dependencies": { - "@types/istanbul-lib-report": "*" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", - "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "node_modules/@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==" - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "node_modules/@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, "dependencies": { - "@types/yargs-parser": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", - "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/type-utils": "6.12.0", - "@typescript-eslint/utils": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@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, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", - "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", + "node_modules/@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, - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/typescript-estree": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", - "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "node_modules/@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, "dependencies": { - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", - "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", + "node_modules/@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, "dependencies": { - "@typescript-eslint/typescript-estree": "6.12.0", - "@typescript-eslint/utils": "6.12.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/types": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", - "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "node_modules/@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, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", - "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "node_modules/@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, "dependencies": { - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", - "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/typescript-estree": "6.12.0", - "semver": "^7.5.4" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@babel/traverse/node_modules/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, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", - "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.12.0", - "eslint-visitor-keys": "^3.4.1" + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "node_modules/@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 }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "optional": true, "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=0.1.90" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/@commitlint/cli": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz", + "integrity": "sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg==", "dev": true, "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@commitlint/format": "^17.8.1", + "@commitlint/lint": "^17.8.1", + "@commitlint/load": "^17.8.1", + "@commitlint/read": "^17.8.1", + "@commitlint/types": "^17.8.1", + "execa": "^5.0.0", + "lodash.isfunction": "^3.0.9", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@commitlint/config-conventional": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz", + "integrity": "sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "conventional-changelog-conventionalcommits": "^6.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "engines": { - "node": ">=6" + "node": ">=v14" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@commitlint/config-validator": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz", + "integrity": "sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "@commitlint/types": "^17.8.1", + "ajv": "^8.11.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=v14" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/@commitlint/ensure": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz", + "integrity": "sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "@commitlint/types": "^17.8.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/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==", - "dependencies": { - "color-convert": "^1.9.0" - }, + "node_modules/@commitlint/execute-rule": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz", + "integrity": "sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=v14" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@commitlint/format": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz", + "integrity": "sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@commitlint/types": "^17.8.1", + "chalk": "^4.1.0" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/apisauce": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/apisauce/-/apisauce-2.1.5.tgz", - "integrity": "sha512-bkMlz0ZUnyS8vDigej9UBYo5dne9/bQrkgIiIkGaiDHF6e5OxhYRLJDYu65V/Ox86tmWVwepIntAoTmk4Db0Hg==", - "dependencies": { - "axios": "^0.21.4" + "node": ">=v14" } }, - "node_modules/app-module-path": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", - "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@commitlint/is-ignored": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz", + "integrity": "sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g==", "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true, + "@commitlint/types": "^17.8.1", + "semver": "7.5.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=v14" } }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true - }, - "node_modules/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==", + "node_modules/@commitlint/lint": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz", + "integrity": "sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA==", "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^17.8.1", + "@commitlint/parse": "^17.8.1", + "@commitlint/rules": "^17.8.1", + "@commitlint/types": "^17.8.1" + }, "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "node_modules/@commitlint/load": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz", + "integrity": "sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA==", "dev": true, + "dependencies": { + "@commitlint/config-validator": "^17.8.1", + "@commitlint/execute-rule": "^17.8.1", + "@commitlint/resolve-extends": "^17.8.1", + "@commitlint/types": "^17.8.1", + "@types/node": "20.5.1", + "chalk": "^4.1.0", + "cosmiconfig": "^8.0.0", + "cosmiconfig-typescript-loader": "^4.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0", + "resolve-from": "^5.0.0", + "ts-node": "^10.8.1", + "typescript": "^4.6.4 || ^5.2.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=v14" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/@commitlint/message": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz", + "integrity": "sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" + "node": ">=v14" } }, - "node_modules/babel-jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.4.2.tgz", - "integrity": "sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ==", + "node_modules/@commitlint/parse": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz", + "integrity": "sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw==", "dev": true, "dependencies": { - "@jest/transform": "^29.4.2", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.4.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "@commitlint/types": "^17.8.1", + "conventional-changelog-angular": "^6.0.0", + "conventional-commits-parser": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "node": ">=v14" } }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@commitlint/read": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz", + "integrity": "sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@commitlint/top-level": "^17.8.1", + "@commitlint/types": "^17.8.1", + "fs-extra": "^11.0.0", + "git-raw-commits": "^2.0.11", + "minimist": "^1.2.6" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=v14" } }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@commitlint/resolve-extends": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz", + "integrity": "sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@commitlint/config-validator": "^17.8.1", + "@commitlint/types": "^17.8.1", + "import-fresh": "^3.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=v14" } }, - "node_modules/babel-jest/node_modules/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==", + "node_modules/@commitlint/rules": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz", + "integrity": "sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@commitlint/ensure": "^17.8.1", + "@commitlint/message": "^17.8.1", + "@commitlint/to-lines": "^17.8.1", + "@commitlint/types": "^17.8.1", + "execa": "^5.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=v14" } }, - "node_modules/babel-jest/node_modules/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 - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@commitlint/to-lines": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz", + "integrity": "sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/babel-jest/node_modules/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==", + "node_modules/@commitlint/top-level": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz", + "integrity": "sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "find-up": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "node_modules/@commitlint/types": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.8.1.tgz", + "integrity": "sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" + "chalk": "^4.1.0" }, "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz", - "integrity": "sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/babel-preset-jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz", - "integrity": "sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.4.2", - "babel-preset-current-node-syntax": "^1.0.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { - "node": ">=0.6" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { - "big-integer": "^1.6.44" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "node-int64": "^0.4.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "node_modules/@eslint/eslintrc/node_modules/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 }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "run-applescript": "^5.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "node_modules/@fastify/accept-negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", + "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", "engines": { - "node": ">=6" + "node": ">=14" } }, - "node_modules/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==", - "dev": true, + "node_modules/@fastify/ajv-compiler": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", + "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001452", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", - "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@fastify/compress": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-6.5.0.tgz", + "integrity": "sha512-AqUOK714jY7qkzbQbS4zyI4yNFgnRoOJ3eH/oV1T9f5fFdPDRdrFxm5de1ya5n+as4bvitjwU9EY7zvtT9pI2A==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "@fastify/accept-negotiator": "^1.1.0", + "fastify-plugin": "^4.5.0", + "into-stream": "^6.0.0", + "mime-db": "^1.52.0", + "minipass": "^7.0.2", + "peek-stream": "^1.1.3", + "pump": "^3.0.0", + "pumpify": "^2.0.1" } }, - "node_modules/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, + "node_modules/@fastify/compress/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" + "node_modules/@fastify/cors": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.3.0.tgz", + "integrity": "sha512-oj9xkka2Tg0MrwuKhsSUumcAkfp2YCnKxmFEusi01pjk1YrdDsuSYTHXEelWNW+ilSy/ApZq0c2SvhKrLX0H1g==", + "dependencies": { + "fastify-plugin": "^4.0.0", + "mnemonist": "0.39.5" } }, - "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true + "node_modules/@fastify/deepmerge": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", + "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" + "node_modules/@fastify/error": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "dependencies": { + "fast-json-stringify": "^5.7.0" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "node_modules/@fastify/formbody": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-7.4.0.tgz", + "integrity": "sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==", "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" + "fast-querystring": "^1.0.0", + "fastify-plugin": "^4.0.0" } }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/@fastify/helmet": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-10.1.1.tgz", + "integrity": "sha512-z9abyIlCHPU25llOTyo3uz8F8TJ+uDqtOC4+38dxODPw8Ro9sTZjbm2U7ZIF0IAv3/E0ke6vbUQ4sB376WuKJA==", + "dependencies": { + "fastify-plugin": "^4.2.1", + "helmet": "^6.0.0" } }, - "node_modules/cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "node_modules/@fastify/middie": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@fastify/middie/-/middie-8.3.0.tgz", + "integrity": "sha512-h+zBxCzMlkEkh4fM7pZaSGzqS7P9M0Z6rXnWPdUEPfe7x1BCj++wEk/pQ5jpyYY4pF8AknFqb77n7uwh8HdxEA==", "dependencies": { - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "colors": "^1.1.2" + "@fastify/error": "^3.2.0", + "fastify-plugin": "^4.0.0", + "path-to-regexp": "^6.1.0", + "reusify": "^1.0.4" } }, - "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, + "node_modules/@fastify/middie/node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + }, + "node_modules/@fastify/send": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", + "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", + "devOptional": true, "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@lukeed/ms": "^2.0.1", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "2.0.0", + "mime": "^3.0.0" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, + "node_modules/@fastify/static": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz", + "integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==", + "devOptional": true, + "dependencies": { + "@fastify/accept-negotiator": "^1.0.0", + "@fastify/send": "^2.0.0", + "content-disposition": "^0.5.3", + "fastify-plugin": "^4.0.0", + "glob": "^8.0.1", + "p-limit": "^3.1.0" + } + }, + "node_modules/@fastify/static/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "devOptional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cli-truncate/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" + "node_modules/@fastify/static/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "devOptional": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=10" } }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/@golevelup/ts-jest": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@golevelup/ts-jest/-/ts-jest-0.4.0.tgz", + "integrity": "sha512-ehgllV/xU8PC+yVyEUtTzhiSQKsr7k5Jz74B6dtCaVJz7/Vo7JiaACsCLvD7/iATlJUAEqvBson0OHewD3JDzQ==", "dev": true }, - "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=12" + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/cli-truncate/node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/cli-truncate/node_modules/string-width": { + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", @@ -3353,10 +3795,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -3368,1999 +3810,2328 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@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, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "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" }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "node": ">=8" } }, - "node_modules/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 - }, - "node_modules/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==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "dependencies": { - "color-name": "1.1.3" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">=0.1.90" + "node": ">=8" } }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": "^12.20.0 || >=14" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/commitlint": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/commitlint/-/commitlint-17.4.3.tgz", - "integrity": "sha512-3MGkngRG3x3KY5uKWxgyKK7WU5apelorn4jeJsu8aCotuaoPXYtZX8Ym7a/ZzB19UUuWADnKWVTWBePvweu3aA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, "dependencies": { - "@commitlint/cli": "^17.4.3", - "@commitlint/types": "^17.4.0" - }, - "bin": { - "commitlint": "cli.js" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=v14" + "node": ">=8" } }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "engines": { + "node": ">=8" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "engines": { - "node": ">=12", - "npm": ">=6" + "dependencies": { + "jest-get-type": "^29.6.3" }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=7", - "ts-node": ">=10", - "typescript": ">=3" - } - }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "engines": { - "node": ">= 6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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 - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">= 8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">=6.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "node-notifier": { "optional": true } } }, - "node_modules/debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/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==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, "engines": { - "node": ">=4.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=6.0.0" } }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=6.0.0" } }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.0.0" } }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/@ls-lint/ls-lint": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@ls-lint/ls-lint/-/ls-lint-1.11.2.tgz", + "integrity": "sha512-kX+CCjgNz+NHCaOcFyJLSBLRgAoyOxN18QFLpgucz5ILvbr60BGjwKaoPYTv/rBV/77L+Oz82lpP24mzJ2wGsQ==", + "cpu": [ + "x64", + "arm64" + ], "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, + "os": [ + "darwin", + "linux", + "win32" + ], + "bin": { + "ls-lint": "bin/cli.js" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/default-browser/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "devOptional": true, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, + "node_modules/@mapbox/node-pre-gyp/node_modules/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==", + "dependencies": { + "semver": "^6.0.0" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "dependencies": { - "clone": "^1.0.2" + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, "engines": { - "node": ">=12" + "node": ">=6" + } + }, + "node_modules/@mentormate/node-cli": { + "resolved": "node-cli", + "link": true + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@nestjs/cli": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.5.0.tgz", + "integrity": "sha512-Z7q+3vNsQSG2d2r2Hl/OOj5EpfjVx3OfnJ9+KuAsOdw1sKLm7+Zc6KbhMFTd/eIvfx82ww3Nk72xdmfPYCulWA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", + "@angular-devkit/schematics-cli": "16.0.1", + "@nestjs/schematics": "^9.0.4", + "chalk": "4.1.2", + "chokidar": "3.5.3", + "cli-table3": "0.6.3", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "8.0.0", + "inquirer": "8.2.5", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "os-name": "4.0.1", + "rimraf": "4.4.1", + "shelljs": "0.8.5", + "source-map-support": "0.5.21", + "tree-kill": "1.2.2", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.0.1", + "typescript": "4.9.5", + "webpack": "5.82.1", + "webpack-node-externals": "3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 12.9.0" } }, - "node_modules/detect-indent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", - "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "node_modules/@nestjs/cli/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "engines": { - "node": ">=12.20" + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/detect-newline": { + "node_modules/@nestjs/cli/node_modules/cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, "engines": { "node": ">=8" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "node_modules/@nestjs/cli/node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/@nestjs/cli/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">= 6" } }, - "node_modules/diff-sequences": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.2.tgz", - "integrity": "sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==", + "node_modules/@nestjs/cli/node_modules/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, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@nestjs/cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/@nestjs/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "node_modules/@nestjs/cli/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "is-obj": "^2.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ejs": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", - "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", + "node_modules/@nestjs/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, "dependencies": { - "jake": "^10.6.1" - }, - "bin": { - "ejs": "bin/cli.js" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.295", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz", - "integrity": "sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw==", - "dev": true + "node_modules/@nestjs/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "node_modules/@nestjs/cli/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/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==" - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/@nestjs/cli/node_modules/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, "dependencies": { - "ansi-colors": "^4.1.1" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=8.6" + "node": ">=8" } }, - "node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "node_modules/@nestjs/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, "engines": { - "node": ">=0.12" + "node": ">=14" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/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==", + "node_modules/@nestjs/cli/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "dependencies": { - "is-arrayish": "^0.2.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/@nestjs/cli/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=6" + "node": ">=4.2.0" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.82.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.1.tgz", + "integrity": "sha512-C6uiGQJ+Gt4RyHXXYt+v9f+SN1v83x68URwgxNQ98cvH8kxiuywWGP4XeNZ1paOzZ63aY3cTciCEQJNFUljlLw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.14.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, "engines": { - "node": ">=0.8.0" + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "node_modules/eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", - "dev": true, + "node_modules/@nestjs/common": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.3.tgz", + "integrity": "sha512-Gd6D4IaYj01o14Bwv81ukidn4w3bPHCblMUq+SmUmWLyosK+XQmInCS09SbDDZyL8jy86PngtBLTdhJ2bXSUig==", "dependencies": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "iterare": "1.2.1", + "tslib": "2.5.3", + "uid": "2.0.2" }, - "bin": { - "eslint": "bin/eslint.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "cache-manager": "<=5", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "cache-manager": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/common/node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, + "node_modules/@nestjs/config": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.4.tgz", + "integrity": "sha512-IGdSF+0F9MJO6dCRTEahdxPz4iVijjtolcFBxnY+2QYM3bXYQvAgzskGZi+WkAFJN/VzR3TEp60gN5sI74GxPA==", + "dependencies": { + "dotenv": "16.1.4", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21", + "uuid": "9.0.0" }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^6.0.0 || ^7.2.0" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", + "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, - "node_modules/eslint-config-prettier": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", - "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", - "dev": true, + "node_modules/@nestjs/config/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "uuid": "dist/bin/uuid" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", - "dev": true, + "node_modules/@nestjs/core": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.4.3.tgz", + "integrity": "sha512-Qi63+wi55Jh4sDyaj5Hhx2jOpKqT386aeo+VOKsxnd+Ql9VvkO/FjmuwBGUyzkJt29ENYc+P0Sx/k5LtstNpPQ==", + "hasInstallScript": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "3.2.0", + "tslib": "2.5.3", + "uid": "2.0.2" }, "funding": { - "url": "https://opencollective.com/prettier" + "type": "opencollective", + "url": "https://opencollective.com/nest" }, "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "prettier": ">=3.0.0" + "@nestjs/common": "^9.0.0", + "@nestjs/microservices": "^9.0.0", + "@nestjs/platform-express": "^9.0.0", + "@nestjs/websockets": "^9.0.0", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" }, "peerDependenciesMeta": { - "@types/eslint": { + "@nestjs/microservices": { "optional": true }, - "eslint-config-prettier": { + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { "optional": true } } }, - "node_modules/eslint-plugin-security": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", - "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", - "dev": true, - "dependencies": { - "safe-regex": "^2.1.1" - } + "node_modules/@nestjs/core/node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, - "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "node_modules/@nestjs/mapped-types": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.2.2.tgz", + "integrity": "sha512-3dHxLXs3M0GPiriAcCFFJQHoDFUuzTD5w6JDhE7TyfT89YKpe6tcCCIqOZWdXmt9AZjjK30RkHRSFF+QEnWFQg==", + "peerDependencies": { + "@nestjs/common": "^7.0.8 || ^8.0.0 || ^9.0.0", + "class-transformer": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0", + "class-validator": "^0.11.1 || ^0.12.0 || ^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12" }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, + "node_modules/@nestjs/platform-fastify": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-9.4.3.tgz", + "integrity": "sha512-FgJyYpZQn97oaarzGKTwcKkrRZs9tv2OdwO0/roO0l/ytnG7eigc7Y3Hv/bDcxl2LngAhY3uXMAg3MMueWojsA==", "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "@fastify/cors": "8.3.0", + "@fastify/formbody": "7.4.0", + "@fastify/middie": "8.3.0", + "fastify": "4.18.0", + "light-my-request": "5.9.1", + "path-to-regexp": "3.2.0", + "tslib": "2.5.3" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "type": "opencollective", + "url": "https://opencollective.com/nest" }, "peerDependencies": { - "eslint": ">=5" + "@fastify/static": "^6.0.0", + "@fastify/view": "^7.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/core": "^9.0.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "@fastify/view": { + "optional": true + } } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/@nestjs/platform-fastify/node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, + "node_modules/@nestjs/schematics": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.2.0.tgz", + "integrity": "sha512-wHpNJDPzM6XtZUOB3gW0J6mkFCSJilzCM3XrHI1o0C8vZmFE1snbmkIXNyoi1eV0Nxh1BMymcgz5vIMJgQtTqw==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", + "jsonc-parser": "3.2.0", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "typescript": ">=4.3.5" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node_modules/@nestjs/swagger": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-6.3.0.tgz", + "integrity": "sha512-Gnig189oa1tD+h0BYIfUwhp/wvvmTn6iO3csR2E4rQrDTgCxSxZDlNdfZo3AC+Rmf8u0KX4ZAX1RZN1qXTtC7A==", + "dependencies": { + "@nestjs/mapped-types": "1.2.2", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "4.18.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@fastify/static": "^6.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/core": "^9.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@nestjs/testing": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.4.3.tgz", + "integrity": "sha512-LDT8Ai2eKnTzvnPaJwWOK03qTaFap5uHHsJCv6dL0uKWk6hyF9jms8DjyVaGsaujCaXDG8izl1mDEER0OmxaZA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "tslib": "2.5.3" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^9.0.0", + "@nestjs/core": "^9.0.0", + "@nestjs/microservices": "^9.0.0", + "@nestjs/platform-express": "^9.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/@nestjs/testing/node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", "dev": true }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", "engines": { - "node": ">=10" + "node": ">= 16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/eslint/node_modules/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==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=7.0.0" + "node": ">= 8" } }, - "node_modules/eslint/node_modules/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 - }, - "node_modules/eslint/node_modules/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==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "node_modules/eslint/node_modules/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==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", "dependencies": { - "is-glob": "^4.0.3" + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@noble/hashes": "^1.1.5" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "optional": true, "engines": { - "node": ">=8" + "node": ">=14" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", "dev": true, - "dependencies": { - "argparse": "^2.0.1" + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "url": "https://opencollective.com/unts" } }, - "node_modules/eslint/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" + "type-detect": "4.0.8" } }, - "node_modules/eslint/node_modules/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==", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/eslint/node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@tsconfig/recommended": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.3.tgz", + "integrity": "sha512-+jby/Guq9H8O7NWgCv6X8VAiQE8Dr/nccsCtL74xyHKhu2Knu5EAKmOZj3nLCnLm1KooUzKY+5DsnGVqhM8/wQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.8.0" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@babel/types": "^7.0.0" } }, - "node_modules/eslint/node_modules/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==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/eslint/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, - "engines": { - "node": ">= 0.8.0" + "dependencies": { + "@babel/types": "^7.20.7" } }, - "node_modules/eslint/node_modules/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==", + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/node": "*" } }, - "node_modules/eslint/node_modules/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, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@types/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@types/express": "*" } }, - "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@types/node": "*" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" + "@types/node": "*" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/@types/eslint": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.1.tgz", + "integrity": "sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, - "engines": { - "node": ">=4.0" + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { - "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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" + "node_modules/@types/express-serve-static-core": { + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/expect": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.2.tgz", - "integrity": "sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ==", + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.4.2", - "jest-get-type": "^29.4.2", - "jest-matcher-utils": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@types/node": "*" } }, - "node_modules/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 + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/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 - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { - "reusify": "^1.0.4" + "@types/istanbul-lib-report": "*" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "dependencies": { - "bser": "2.1.1" + "expect": "^29.0.0", + "pretty-format": "^29.0.0" } }, - "node_modules/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==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", + "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "node_modules/@types/passport": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "@types/express": "*" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/@types/passport-jwt": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.13.tgz", + "integrity": "sha512-fjHaC6Bv8EpMMqzTnHP32SXlZGaNfBPC/Po5dmRGYi2Ky7ljXPbGnOy+SxZqa6iZvFgVhoJ1915Re3m93zmcfA==", + "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "@types/express": "*", + "@types/passport": "*" } }, - "node_modules/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==", + "node_modules/@types/pg": { + "version": "8.10.9", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz", + "integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/statuses": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.4.tgz", + "integrity": "sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==", + "dev": true + }, + "node_modules/@types/superagent": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.1.tgz", + "integrity": "sha512-YQyEXA4PgCl7EVOoSAS3o0fyPFU6erv5mMixztQYe1bqbWmmn8c+IrqoxjQeZe4MgwXikgcaZPiI/DsbmOVlzA==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/@types/supertest": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", + "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "@types/superagent": "*" } }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, + "node_modules/@types/validator": { + "version": "13.11.7", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz", + "integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.3.tgz", + "integrity": "sha512-z1ELvMijRL1QmU7QuzDkeYXSF2+dXI0ITKoQsIoVKcNBOiK5RMmWy+pYYxJTHFt8vkpZe7UsvRErQwcxZkjoUw==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@types/yargs-parser": "*" } }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.0.tgz", + "integrity": "sha512-3lqEvQUdCozi6d1mddWqd+kf8KxmGq2Plzx36BlkjuQe3rSTm/O98cLf0A4uDO+a5N1KD2SeEEl6fW97YHY+6w==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/type-utils": "6.18.0", + "@typescript-eslint/utils": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, "engines": { - "node": ">=4.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "debug": { + "typescript": { "optional": true } } }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "node_modules/@typescript-eslint/parser": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.0.tgz", + "integrity": "sha512-v6uR68SFvqhNQT41frCMCQpsP+5vySy6IdgjlzUWoo7ALCnpaWYcz/Ij2k4L8cEsL0wkvOviCMpjmtRtHNOKzA==", "dev": true, + "peer": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=14" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.0.tgz", + "integrity": "sha512-o/UoDT2NgOJ2VfHpfr+KBY2ErWvCySNUIX/X7O9g8Zzt/tXdpfEU43qbNk8LVuWUT2E0ptzTWXh79i74PP0twA==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0" + }, "engines": { - "node": ">=14" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.0.tgz", + "integrity": "sha512-ZeMtrXnGmTcHciJN1+u2CigWEEXgy1ufoxtWcHORt5kGvpjjIlK9MUhzHm4RM8iVy6dqSaZA/6PVkX6+r+ChjQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/utils": "6.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=14.14" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/@typescript-eslint/types": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", + "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", "dev": true, "engines": { - "node": ">= 10.0.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/fs-jetpack": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz", - "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.0.tgz", + "integrity": "sha512-klNvl+Ql4NsBNGB4W9TZ2Od03lm7aGvTbs0wYaFYsplVPhr+oeXjlPZCDI4U9jgJIDK38W1FKhacCFzCC+nbIg==", + "dev": true, "dependencies": { - "minimatch": "^3.0.2", - "rimraf": "^2.6.3" + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/@typescript-eslint/utils": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.0.tgz", + "integrity": "sha512-wiKKCbUeDPGaYEYQh1S580dGxJ/V9HI7K5sbGAVklyf+o5g3O+adnS4UNJajplF4e7z2q0uVBaTdT/yLb4XAVA==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/typescript-estree": "6.18.0", + "semver": "^7.5.4" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/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, - "engines": { - "node": ">=6.9.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/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==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", + "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.0", + "eslint-visitor-keys": "^3.4.1" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/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, - "engines": { - "node": ">=8.0.0" - } + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true }, - "node_modules/get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true }, - "node_modules/git-hooks-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.0.0.tgz", - "integrity": "sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==", + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, - "funding": { - "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" - }, - "engines": { - "node": ">=10" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "@xtuc/long": "4.2.2" } }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/globby": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", - "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, - "node_modules/globby/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/gluegun": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/gluegun/-/gluegun-5.1.2.tgz", - "integrity": "sha512-Cwx/8S8Z4YQg07a6AFsaGnnnmd8mN17414NcPS3OoDtZRwxgsvwRNJNg69niD6fDa8oNwslCG0xH7rEpRNNE/g==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dev": true, "dependencies": { - "apisauce": "^2.1.5", - "app-module-path": "^2.2.0", - "cli-table3": "0.6.0", - "colors": "1.4.0", - "cosmiconfig": "7.0.1", - "cross-spawn": "7.0.3", - "ejs": "3.1.6", - "enquirer": "2.3.6", - "execa": "5.1.1", - "fs-jetpack": "4.3.1", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.lowercase": "^4.3.0", - "lodash.lowerfirst": "^4.3.1", - "lodash.pad": "^4.5.1", - "lodash.padend": "^4.6.1", - "lodash.padstart": "^4.6.1", - "lodash.repeat": "^4.1.0", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.trim": "^4.5.1", - "lodash.trimend": "^4.5.1", - "lodash.trimstart": "^4.5.1", - "lodash.uppercase": "^4.3.0", - "lodash.upperfirst": "^4.3.1", - "ora": "4.0.2", - "pluralize": "^8.0.0", - "semver": "7.3.5", - "which": "2.0.2", - "yargs-parser": "^21.0.0" - }, - "bin": { - "gluegun": "bin/gluegun" + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/@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 }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "node_modules/@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 }, - "node_modules/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==", - "dev": true, - "engines": { - "node": ">=6" - } + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dependencies": { - "function-bind": "^1.1.1" + "event-target-shim": "^5.0.0" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" + "node": ">=6.5" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/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 + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" }, - "node_modules/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==", + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, "engines": { - "node": ">=10.17.0" + "node": ">= 0.6" } }, - "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { - "husky": "lib/bin.js" + "acorn": "bin/acorn" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" + "node": ">=0.4.0" } }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, - "engines": { - "node": ">= 4" + "peerDependencies": { + "acorn": "^8" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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==", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "debug": "4" }, "engines": { - "node": ">=6" + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "ajv": "^8.0.0" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "engines": { - "node": ">=0.8.19" + "node": ">=6" } }, - "node_modules/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==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, "engines": { "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, - "dependencies": { - "has": "^1.0.3" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "bin": { - "is-docker": "cli.js" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/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==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/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==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, "engines": { - "node": ">=6" + "node": ">= 8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "node_modules/apisauce": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/apisauce/-/apisauce-2.1.6.tgz", + "integrity": "sha512-MdxR391op/FucS2YQRfB/NMRyCnHEPDd4h17LRIuVYi0BpGmMhpxc0shbOpfs5ahABuBEffNCGal5EcsydbBWg==", "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" + "axios": "^0.21.4" } }, - "node_modules/is-inside-container": { + "node_modules/app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/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==", - "engines": { - "node": ">=8" - } + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/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==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/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==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 4.0.0" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.0.0" } }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dev": true, + "node_modules/avvio": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.1.tgz", + "integrity": "sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==", "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.6.1" } }, - "node_modules/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, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" + "follow-redirects": "^1.14.0" } }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, - "bin": { - "is-docker": "cli.js" + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-instrument": { + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", @@ -5376,399 +6147,460 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, - "node_modules/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==", + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/istanbul-lib-report/node_modules/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==", + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">= 10.0.0" } }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "node_modules/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, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "node_modules/bl/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" + "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" } }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "node_modules/bl/node_modules/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==", + "dev": true }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/bl/node_modules/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, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "safe-buffer": "~5.1.0" } }, - "node_modules/jake/node_modules/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==", + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "color-name": "~1.1.4" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/jake/node_modules/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==" - }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/jake/node_modules/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==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "ms": "2.0.0" } }, - "node_modules/jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.2.tgz", - "integrity": "sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg==", - "dev": true, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { - "@jest/core": "^29.4.2", - "@jest/types": "^29.4.2", - "import-local": "^3.0.2", - "jest-cli": "^29.4.2" - }, - "bin": { - "jest": "bin/jest.js" + "side-channel": "^1.0.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": ">=0.6" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-changed-files": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.4.2.tgz", - "integrity": "sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw==", - "dev": true, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/jest-changed-files/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/jest-circus": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.4.2.tgz", - "integrity": "sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg==", + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "@jest/environment": "^29.4.2", - "@jest/expect": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.4.2", - "jest-matcher-utils": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-runtime": "^29.4.2", - "jest-snapshot": "^29.4.2", - "jest-util": "^29.4.2", - "p-limit": "^3.1.0", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/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, "dependencies": { - "color-convert": "^2.0.1" + "fast-json-stable-stringify": "2.x" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 6" } }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/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, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node-int64": "^0.4.0" + } + }, + "node_modules/bson": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", + "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=16.20.1" } }, - "node_modules/jest-circus/node_modules/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==", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/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==", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" } }, - "node_modules/jest-circus/node_modules/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==", + "node_modules/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==", "dev": true }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/jest-circus/node_modules/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, - "dependencies": { - "yocto-queue": "^0.1.0" - }, + "node_modules/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": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/jest-circus/node_modules/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==", + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "has-flag": "^4.0.0" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/jest-cli": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.4.2.tgz", - "integrity": "sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q==", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "dependencies": { - "@jest/core": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/types": "^29.4.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.4.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=6" } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/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==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/node_modules/chalk": { + "node_modules/caniuse-lite": { + "version": "1.0.30001574", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz", + "integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5780,211 +6612,288 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-cli/node_modules/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==", + "node_modules/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, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=10" } }, - "node_modules/jest-cli/node_modules/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==", + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">=8" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/jest-cli/node_modules/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==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/jest-config": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.4.2.tgz", - "integrity": "sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA==", + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.4.2", - "@jest/types": "^29.4.2", - "babel-jest": "^29.4.2", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.4.2", - "jest-environment-node": "^29.4.2", - "jest-get-type": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-runner": "^29.4.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" } + ], + "engines": { + "node": ">=8" } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", + "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==", + "dependencies": { + "@types/validator": "^13.7.10", + "libphonenumber-js": "^1.10.14", + "validator": "^13.7.0" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "object-assign": "^4.1.0", + "string-width": "^4.2.0" }, "engines": { - "node": ">=10" + "node": "10.* || >= 12.*" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "optionalDependencies": { + "colors": "^1.1.2" } }, - "node_modules/jest-config/node_modules/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==", + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" }, "engines": { - "node": ">=7.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/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 - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/jest-config/node_modules/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==", + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-diff": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.2.tgz", - "integrity": "sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g==", + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.2", - "jest-get-type": "^29.4.2", - "pretty-format": "^29.4.2" + "ansi-regex": "^6.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/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, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/jest-diff/node_modules/color-convert": { + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/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, "dependencies": { "color-name": "~1.1.4" }, @@ -5992,2120 +6901,2488 @@ "node": ">=7.0.0" } }, - "node_modules/jest-diff/node_modules/color-name": { + "node_modules/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==", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, - "node_modules/jest-diff/node_modules/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, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "engines": { - "node": ">=8" + "node": ">=0.1.90" } }, - "node_modules/jest-diff/node_modules/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, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "has-flag": "^4.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/jest-docblock": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.2.tgz", - "integrity": "sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag==", + "node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=16" } }, - "node_modules/jest-each": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.4.2.tgz", - "integrity": "sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.4.2", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.2", - "jest-util": "^29.4.2", - "pretty-format": "^29.4.2" + "node_modules/commitizen": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.0.tgz", + "integrity": "sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==", + "dev": true, + "dependencies": { + "cachedir": "2.3.0", + "cz-conventional-changelog": "3.3.0", + "dedent": "0.7.0", + "detect-indent": "6.1.0", + "find-node-modules": "^2.1.2", + "find-root": "1.1.0", + "fs-extra": "9.1.0", + "glob": "7.2.3", + "inquirer": "8.2.5", + "is-utf8": "^0.2.1", + "lodash": "4.17.21", + "minimist": "1.2.7", + "strip-bom": "4.0.0", + "strip-json-comments": "3.1.1" + }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 12" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/commitizen/node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/commitizen/node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/commitizen/node_modules/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==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" } }, - "node_modules/jest-each/node_modules/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 - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/commitizen/node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/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==", + "node_modules/commitlint": { + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/commitlint/-/commitlint-17.8.1.tgz", + "integrity": "sha512-X+VPJwZsQDeGj/DG1NsxhZEl+oMHKNC+1myZ/zauNDoo+7OuLHfTOUU1C1a4CjKW4b6T7NuoFcYfK0kRCjCtbA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@commitlint/cli": "^17.8.1", + "@commitlint/types": "^17.8.1" + }, + "bin": { + "commitlint": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=v14" } }, - "node_modules/jest-environment-node": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.4.2.tgz", - "integrity": "sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w==", + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "dependencies": { - "@jest/environment": "^29.4.2", - "@jest/fake-timers": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "jest-mock": "^29.4.2", - "jest-util": "^29.4.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "node_modules/jest-get-type": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.2.tgz", - "integrity": "sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg==", + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-haste-map": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.4.2.tgz", - "integrity": "sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw==", - "dev": true, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dependencies": { - "@jest/types": "^29.4.2", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.2", - "jest-util": "^29.4.2", - "jest-worker": "^29.4.2", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": ">= 0.6" } }, - "node_modules/jest-leak-detector": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz", - "integrity": "sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA==", - "dev": true, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dependencies": { - "jest-get-type": "^29.4.2", - "pretty-format": "^29.4.2" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.8.0" } }, - "node_modules/jest-matcher-utils": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz", - "integrity": "sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg==", - "dev": true, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.4.2", - "jest-get-type": "^29.4.2", - "pretty-format": "^29.4.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ms": "2.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/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, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/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==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "color-convert": "^2.0.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=14" } }, - "node_modules/jest-matcher-utils/node_modules/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==", + "node_modules/conventional-changelog-conventionalcommits": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", + "integrity": "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=14" } }, - "node_modules/jest-matcher-utils/node_modules/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==", + "node_modules/conventional-commit-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", + "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", "dev": true }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { + "node_modules/conventional-commits-parser": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, "engines": { - "node": ">=8" + "node": ">=14" } }, - "node_modules/jest-matcher-utils/node_modules/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, - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/jest-message-util": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.2.tgz", - "integrity": "sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g==", - "dev": true, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.4.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "object-assign": "^4", + "vary": "^1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.10" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz", + "integrity": "sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">=v14.21.3" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "ts-node": ">=10", + "typescript": ">=4" } }, - "node_modules/jest-message-util/node_modules/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==", + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" }, "engines": { - "node": ">=7.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-message-util/node_modules/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==", + "node_modules/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 }, - "node_modules/jest-message-util/node_modules/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, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/jest-message-util/node_modules/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==", + "node_modules/cz-conventional-changelog": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", + "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" }, "engines": { - "node": ">=8" + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" } }, - "node_modules/jest-mock": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.4.2.tgz", - "integrity": "sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g==", + "node_modules/cz-conventional-changelog/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "@jest/types": "^29.4.2", - "@types/node": "*", - "jest-util": "^29.4.2" + "color-convert": "^1.9.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "node_modules/cz-conventional-changelog/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "engines": { + "node": ">=4" } }, - "node_modules/jest-regex-util": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.2.tgz", - "integrity": "sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig==", + "node_modules/cz-conventional-changelog/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/cz-conventional-changelog/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.8.0" } }, - "node_modules/jest-resolve": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.4.2.tgz", - "integrity": "sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw==", + "node_modules/cz-conventional-changelog/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/jest-resolve-dependencies": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz", - "integrity": "sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg==", + "node_modules/cz-conventional-changelog/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.2", - "jest-snapshot": "^29.4.2" + "has-flag": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ms": "2.1.2" }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/jest-resolve/node_modules/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==", + "node_modules/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": "*" } }, - "node_modules/jest-resolve/node_modules/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 - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-resolve/node_modules/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==", + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-runner": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.4.2.tgz", - "integrity": "sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg==", + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, - "dependencies": { - "@jest/console": "^29.4.2", - "@jest/environment": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.2", - "jest-environment-node": "^29.4.2", - "jest-haste-map": "^29.4.2", - "jest-leak-detector": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-runtime": "^29.4.2", - "jest-util": "^29.4.2", - "jest-watcher": "^29.4.2", - "jest-worker": "^29.4.2", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" } }, - "node_modules/jest-runner/node_modules/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==", + "node_modules/decompress-tar/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" }, "engines": { - "node": ">=7.0.0" + "node": ">=4" } }, - "node_modules/jest-runner/node_modules/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 + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/decompress-tarbz2/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-runner/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/jest-runner/node_modules/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==", + "node_modules/decompress-targz/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/jest-runtime": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.4.2.tgz", - "integrity": "sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.4.2", - "@jest/fake-timers": "^29.4.2", - "@jest/globals": "^29.4.2", - "@jest/source-map": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-mock": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-snapshot": "^29.4.2", - "jest-util": "^29.4.2", - "semver": "^7.3.5", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/decompress-unzip/node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/decompress/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "pify": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" } }, - "node_modules/jest-runtime/node_modules/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==", + "node_modules/decompress/node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=4" } }, - "node_modules/jest-runtime/node_modules/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 + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/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==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4.0.0" } }, - "node_modules/jest-runtime/node_modules/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==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dependencies": { - "has-flag": "^4.0.0" + "clone": "^1.0.2" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-snapshot": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.4.2.tgz", - "integrity": "sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw==", - "dev": true, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.4.2", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.4.2", - "jest-get-type": "^29.4.2", - "jest-haste-map": "^29.4.2", - "jest-matcher-utils": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2", - "natural-compare": "^1.4.0", - "pretty-format": "^29.4.2", - "semver": "^7.3.5" + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-snapshot/node_modules/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, - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { - "node": ">=7.0.0" + "node": ">=0.4.0" } }, - "node_modules/jest-snapshot/node_modules/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 + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, - "node_modules/jest-snapshot/node_modules/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, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/jest-snapshot/node_modules/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, - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { - "node": ">=8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/jest-util": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.2.tgz", - "integrity": "sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==", + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true, - "dependencies": { - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/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, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/jest-util/node_modules/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==", + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "asap": "^2.0.0", + "wrappy": "1" } }, - "node_modules/jest-util/node_modules/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 + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-util/node_modules/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==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/jest-validate": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.4.2.tgz", - "integrity": "sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "@jest/types": "^29.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.2", - "leven": "^3.1.0", - "pretty-format": "^29.4.2" + "esutils": "^2.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "is-obj": "^2.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12" } }, - "node_modules/jest-validate/node_modules/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, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, - "node_modules/jest-validate/node_modules/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 + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "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" + } }, - "node_modules/jest-validate/node_modules/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, - "engines": { - "node": ">=8" + "node_modules/duplexify/node_modules/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==" + }, + "node_modules/duplexify/node_modules/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==", + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/jest-validate/node_modules/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, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/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==", "dependencies": { - "has-flag": "^4.0.0" + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-watcher": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.4.2.tgz", - "integrity": "sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w==", + "node_modules/electron-to-chromium": { + "version": "1.4.623", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz", + "integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, - "dependencies": { - "@jest/test-result": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.4.2", - "string-length": "^4.0.1" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/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==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/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==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10.13.0" } }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-colors": "^4.1.1" }, "engines": { - "node": ">=10" + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "engines": { + "node": ">=0.12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/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, + "node_modules/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==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "is-arrayish": "^0.2.1" } }, - "node_modules/jest-watcher/node_modules/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==", + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "dev": true + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, - "node_modules/jest-watcher/node_modules/has-flag": { + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-watcher/node_modules/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==", + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-worker": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.2.tgz", - "integrity": "sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw==", + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz", + "integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==", "dev": true, "dependencies": { - "@types/node": "*", - "jest-util": "^29.4.2", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/eslint-plugin-security": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", + "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "safe-regex": "^2.1.1" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "url": "https://opencollective.com/eslint" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/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==" - }, - "node_modules/json-schema-traverse": { + "node_modules/eslint/node_modules/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 }, - "node_modules/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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "universalify": "^2.0.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=4" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, - "engines": [ - "node >= 0.2.0" - ] + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "node_modules/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, "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" + "estraverse": "^5.2.0" }, "engines": { - "node": "*" + "node": ">=4.0" } }, - "node_modules/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==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/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, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "engines": { "node": ">=6" } }, - "node_modules/license-checker": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", - "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", - "dev": true, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dependencies": { - "chalk": "^2.4.1", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "read-installed": "~4.0.3", - "semver": "^5.5.0", - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-satisfies": "^4.0.0", - "treeify": "^1.1.0" + "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" }, - "bin": { - "license-checker": "bin/license-checker" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/license-checker/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/license-checker/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } }, - "node_modules/linkify-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", - "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", - "dev": true, + "node_modules/express-jwt": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.4.1.tgz", + "integrity": "sha512-IZoZiDv2yZJAb3QrbaSATVtTCYT11OcqgFGoTN4iKVyN6NBkBkhtVIixww5fmakF0Upt5HfOxJuS6ZmJVeOtTQ==", "dependencies": { - "uc.micro": "^1.0.1" + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" } }, - "node_modules/lint-staged": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.2.tgz", - "integrity": "sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==", - "dev": true, + "node_modules/express-oauth2-jwt-bearer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/express-oauth2-jwt-bearer/-/express-oauth2-jwt-bearer-1.6.0.tgz", + "integrity": "sha512-HXnez7vocYlOqlfF3ozPcf/WE3zxT7zfUNfeg5FHJnvNwhBYlNXiPOvuCtBalis8xcigvwtInzEKhBuH87+9ug==", "dependencies": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.19", - "commander": "^9.4.1", - "debug": "^4.3.4", - "execa": "^6.1.0", - "lilconfig": "2.0.6", - "listr2": "^5.0.5", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.2", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.1.3" + "jose": "^4.13.1" }, - "bin": { - "lint-staged": "bin/lint-staged.js" + "engines": { + "node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0 || ^20.2.0" + } + }, + "node_modules/express-pg-auth0": { + "resolved": "express-pg-auth0", + "link": true + }, + "node_modules/express-pg-jwt": { + "resolved": "express-pg-jwt", + "link": true + }, + "node_modules/express-unless": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", + "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=0.6" }, "funding": { - "url": "https://opencollective.com/lint-staged" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lint-staged/node_modules/execa": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", - "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "node_modules/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, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^3.0.1", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=4" } }, - "node_modules/lint-staged/node_modules/human-signals": { + "node_modules/fast-content-type-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" + }, + "node_modules/fast-copy": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", - "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", + "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==", + "dev": true + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, + "node_modules/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==" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, "engines": { - "node": ">=12.20.0" + "node": ">=8.6.0" } }, - "node_modules/lint-staged/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 6" + } + }, + "node_modules/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 + }, + "node_modules/fast-json-stringify": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.10.0.tgz", + "integrity": "sha512-fu1BhzPzgOdvK+sVhSPFzm06DQl0Dwbo+NQxWm21k03ili2wsJExXbGZ9qsD4Lsn7zFGltF8h9I1fuhk4JPnrQ==", + "dependencies": { + "@fastify/deepmerge": "^1.0.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "dependencies": { + "boolean": "^3.1.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=10.0" } }, - "node_modules/lint-staged/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", + "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" + }, + "node_modules/fastify": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.18.0.tgz", + "integrity": "sha512-L5o/2GEkBastQ3HV0dtKo7SUZ497Z1+q4fcqAoPyq6JCQ/8zdk1JQEoTQwnBWCp+EmA7AQa6mxNqSAEhzP0RwQ==", + "dependencies": { + "@fastify/ajv-compiler": "^3.5.0", + "@fastify/error": "^3.2.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.2.1", + "fast-content-type-parse": "^1.0.0", + "fast-json-stringify": "^5.7.0", + "find-my-way": "^7.6.0", + "light-my-request": "^5.9.1", + "pino": "^8.12.0", + "process-warning": "^2.2.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.5.0", + "semver": "^7.5.0", + "tiny-lru": "^11.0.1" + } + }, + "node_modules/fastify-plugin": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bser": "2.1.1" } }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "pend": "~1.2.0" } }, - "node_modules/lint-staged/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, - "node_modules/listr2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.7.tgz", - "integrity": "sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==", + "node_modules/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, "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "flat-cache": "^3.0.4" }, "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dependencies": { - "color-convert": "^2.0.1" + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10" } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "node_modules/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==", "dev": true, "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" + "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8" } }, - "node_modules/listr2/node_modules/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, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "color-name": "~1.1.4" + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-my-way": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.7.0.tgz", + "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^2.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=14" } }, - "node_modules/listr2/node_modules/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==", + "node_modules/find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "dev": true }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "node_modules/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, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==" - }, - "node_modules/lodash.lowercase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz", - "integrity": "sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA==" - }, - "node_modules/lodash.lowerfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz", - "integrity": "sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true - }, - "node_modules/lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==" - }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" - }, - "node_modules/lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==" - }, - "node_modules/lodash.repeat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", - "integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==" - }, - "node_modules/lodash.trim": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", - "integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==" - }, - "node_modules/lodash.trimend": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz", - "integrity": "sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA==" - }, - "node_modules/lodash.trimstart": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz", - "integrity": "sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true - }, - "node_modules/lodash.uppercase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz", - "integrity": "sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA==" - }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==" - }, - "node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dependencies": { - "chalk": "^2.4.2" - }, + "node_modules/follow-redirects": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { - "node": ">=8" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/log-update/node_modules/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==", + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", + "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=12.13.0", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" } }, - "node_modules/log-update/node_modules/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 + "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/log-update/node_modules/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==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/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==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/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==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">= 6" } }, - "node_modules/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 - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { - "tmpl": "1.0.5" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/markdown-it": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", - "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" } }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/markdownlint": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.28.2.tgz", - "integrity": "sha512-yYaQXoKKPV1zgrFsyAuZPEQoe+JrY9GDag9ObKpk09twx4OCU5lut+0/kZPrQ3W7w82SmgKhd7D8m34aG1unVw==", - "dev": true, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dependencies": { - "markdown-it": "13.0.1", - "markdownlint-micromark": "0.1.2" - }, - "engines": { - "node": ">=14.18.0" + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" } }, - "node_modules/markdownlint-cli": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.34.0.tgz", - "integrity": "sha512-4G9I++VBTZkaye6Yfc/7dU6HQHcyldZEVB+bYyQJLcpJOHKk/q5ZpGqK80oKMIdlxzsA3aWOJLZ4DkoaoUWXbQ==", - "dev": true, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { - "commander": "~10.0.1", - "get-stdin": "~9.0.0", - "glob": "~10.2.2", - "ignore": "~5.2.4", - "js-yaml": "^4.1.0", - "jsonc-parser": "~3.2.0", - "markdownlint": "~0.28.2", - "minimatch": "~9.0.0", - "run-con": "~1.2.11" - }, - "bin": { - "markdownlint": "markdownlint.js" - }, - "engines": { - "node": ">=14" + "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" } }, - "node_modules/markdownlint-cli/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/from2/node_modules/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==" + }, + "node_modules/from2/node_modules/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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/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==", "dev": true }, - "node_modules/markdownlint-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">=14" + "node": ">=14.14" } }, - "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz", - "integrity": "sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==", - "dev": true, + "node_modules/fs-jetpack": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz", + "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" + "minimatch": "^3.0.2", + "rimraf": "^2.6.3" + } + }, + "node_modules/fs-jetpack/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/fs-jetpack/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, - "node_modules/markdownlint-cli/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "node_modules/fs-jetpack/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dependencies": { - "argparse": "^2.0.1" + "glob": "^7.1.3" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "rimraf": "bin.js" } }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dependencies": { - "brace-expansion": "^2.0.1" + "minipass": "^3.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 8" } }, - "node_modules/markdownlint-micromark": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", - "integrity": "sha512-jRxlQg8KpOfM2IbCL9RXM8ZiYWz2rv6DlZAnGv8ASJQpUh6byTBnEsbuMZ6T2/uIgntyf7SKg/mEaEBo1164fQ==", - "dev": true, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=14.18.0" + "node": ">=8" } }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", "dev": true }, - "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "dependencies": { - "@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" - }, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" - }, + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" }, "engines": { "node": ">=10" } }, - "node_modules/meow/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/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==", "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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==", "engines": { - "node": ">=10" + "node": ">=8.0.0" } }, - "node_modules/meow/node_modules/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==", + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/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==" + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/git-hooks-list": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.1.0.tgz", + "integrity": "sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==", "dev": true, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" }, "engines": { - "node": ">=8.6" + "node": ">=10" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/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==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "brace-expansion": "^1.1.7" + "is-glob": "^4.0.3" }, "engines": { - "node": "*" + "node": ">=10.13.0" } }, - "node_modules/minimatch/node_modules/brace-expansion": { + "node_modules/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 + }, + "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", @@ -8114,544 +9391,529 @@ "concat-map": "0.0.1" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" + "node": "*" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=10.0" } }, - "node_modules/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 - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", "dev": true, "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" + "ini": "^1.3.4" }, - "bin": { - "nopt": "bin/nopt.js" + "engines": { + "node": ">=4" } }, - "node_modules/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==", + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/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, + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/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==", - "dev": true - }, - "node_modules/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==", + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, "dependencies": { - "path-key": "^3.0.0" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { - "wrappy": "1" + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=6" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "define-properties": "^1.1.3" }, "engines": { - "node": ">=14.16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ora": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", - "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "dependencies": { - "chalk": "^2.4.2", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.2.0", - "is-interactive": "^1.0.0", - "log-symbols": "^3.0.0", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" + "node": ">=10" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, + "node_modules/gluegun": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/gluegun/-/gluegun-5.1.6.tgz", + "integrity": "sha512-9zbi4EQWIVvSOftJWquWzr9gLX2kaDgPkNR5dYWbM53eVvCI3iKuxLlnKoHC0v4uPoq+Kr/+F569tjoFbA4DSA==", "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "apisauce": "^2.1.5", + "app-module-path": "^2.2.0", + "cli-table3": "0.6.0", + "colors": "1.4.0", + "cosmiconfig": "7.0.1", + "cross-spawn": "7.0.3", + "ejs": "3.1.8", + "enquirer": "2.3.6", + "execa": "5.1.1", + "fs-jetpack": "4.3.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.lowercase": "^4.3.0", + "lodash.lowerfirst": "^4.3.1", + "lodash.pad": "^4.5.1", + "lodash.padend": "^4.6.1", + "lodash.padstart": "^4.6.1", + "lodash.repeat": "^4.1.0", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.trim": "^4.5.1", + "lodash.trimend": "^4.5.1", + "lodash.trimstart": "^4.5.1", + "lodash.uppercase": "^4.3.0", + "lodash.upperfirst": "^4.3.1", + "ora": "4.0.2", + "pluralize": "^8.0.0", + "semver": "7.3.5", + "which": "2.0.2", + "yargs-parser": "^21.0.0" + }, + "bin": { + "gluegun": "bin/gluegun" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/gluegun/node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dependencies": { - "p-try": "^2.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/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, + "node_modules/gluegun/node_modules/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==", "dependencies": { - "p-limit": "^2.2.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/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==", - "dev": true, + "node_modules/gluegun/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dependencies": { - "aggregate-error": "^3.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/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==", - "dev": true, + "node_modules/gluegun/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/gluegun/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "engines": { - "node": ">=6" + "node": ">= 6" } }, - "node_modules/parent-module": { + "node_modules/gopd": { "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==", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { - "callsites": "^3.0.0" + "get-intrinsic": "^1.1.3" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/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==", + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dependencies": { - "@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" + "get-intrinsic": "^1.2.2" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/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, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/helmet": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.2.0.tgz", + "integrity": "sha512-DWlwuXLLqbrIOltR6tFQXShj/+7Cyp0gLi6uAb8qMdFh/YBBFbKSgQ6nbXmScYd8emMctuthmgIa7tUfo9Rtyg==", "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "node_modules/help-me": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", + "integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==", + "dev": true, + "dependencies": { + "glob": "^8.0.0", + "readable-stream": "^3.6.0" + } }, - "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "node_modules/help-me/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "node_modules/help-me/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": "14 || >=16.14" + "node": ">=10" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, - "engines": { - "node": ">=8.6" + "dependencies": { + "parse-passwd": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" + "dependencies": { + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=10" } }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "node_modules/hosted-git-info/node_modules/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==", "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=10" } }, - "node_modules/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==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "engines": { - "node": ">=4" - } + "node_modules/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 }, - "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">= 0.8" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, + "node_modules/http-terminator": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/http-terminator/-/http-terminator-3.2.0.tgz", + "integrity": "sha512-JLjck1EzPaWjsmIf8bziM3p9fgR1Y3JoUKAkyYEbZmFrIvJM6I8vVJfBGWlEtV9IWOvzNnaTtjuwZeBY2kwB4g==", "dependencies": { - "fast-diff": "^1.1.2" + "delay": "^5.0.0", + "p-wait-for": "^3.2.0", + "roarr": "^7.0.4", + "type-fest": "^2.3.3" }, "engines": { - "node": ">=6.0.0" + "node": ">=14" } }, - "node_modules/pretty-format": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.2.tgz", - "integrity": "sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg==", - "dev": true, + "node_modules/http-terminator/node_modules/roarr": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-7.21.0.tgz", + "integrity": "sha512-d1rPLcHmQID3GsA3p9d5vKSZYlvrTWhjbmeg9DT5DcPoLpH85VzPmkLkGKhQv376+dfkApaHwNbpYEwDB77Ibg==", "dependencies": { - "@jest/schemas": "^29.4.2", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "fast-printf": "^1.6.9", + "safe-stable-stringify": "^2.4.3", + "semver-compare": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "node_modules/http-terminator/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "agent-base": "6", + "debug": "4" }, "engines": { "node": ">= 6" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, + "node_modules/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==", "engines": { - "node": ">=6" + "node": ">=10.17.0" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", "dev": true, + "bin": { + "husky": "lib/bin.js" + }, "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -8667,83 +9929,55 @@ } ] }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, - "node_modules/read-installed": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", - "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", - "dev": true, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dependencies": { - "debuglog": "^1.0.1", - "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" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.2" - } - }, - "node_modules/read-installed/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-package-json": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", - "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", - "dev": true, - "dependencies": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/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, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, + "node_modules/import-fresh/node_modules/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==", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/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==", + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { "node": ">=8" @@ -8752,8590 +9986,6869 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg/node_modules/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==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.8.19" } }, - "node_modules/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==", + "node_modules/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==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", "dev": true, "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=12.0.0" } }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "node_modules/inquirer/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "bin": { - "regexp-tree": "bin/regexp-tree" + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/inquirer/node_modules/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, + "dependencies": { + "restore-cursor": "^3.1.0" + }, "engines": { "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/inquirer/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/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==", + "node_modules/inquirer/node_modules/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, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/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==", + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/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, "dependencies": { - "resolve-from": "^5.0.0" + "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/resolve-from": { + "node_modules/is-fullwidth-code-point": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "global-dirs": "^0.1.1" + "is-extglob": "^2.1.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", "engines": { "node": ">=8" } }, - "node_modules/resolve.exports": { + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz", - "integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "engines": { - "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" }, - "bin": { - "rimraf": "bin.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", "dev": true, "dependencies": { - "execa": "^5.0.0" + "text-extensions": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-con": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz", - "integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==", + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/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, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~3.0.0", - "minimist": "^1.2.6", - "strip-json-comments": "~3.1.1" - }, - "bin": { - "run-con": "cli.js" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/run-con/node_modules/ini": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", - "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "queue-microtask": "^1.2.2" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" } }, - "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "tslib": "^2.1.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "dependencies": { - "regexp-tree": "~0.1.1" - } - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { "node": ">=10" } }, - "node_modules/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==", + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/slice-ansi/node_modules/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, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dependencies": { - "color-convert": "^2.0.1" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, - "engines": { - "node": ">=8" + "bin": { + "jake": "bin/cli.js" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=10" } }, - "node_modules/slice-ansi/node_modules/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, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/slice-ansi/node_modules/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 - }, - "node_modules/slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", - "dev": true, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { "node": "*" } }, - "node_modules/sort-object-keys": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", - "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", - "dev": true - }, - "node_modules/sort-package-json": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.4.1.tgz", - "integrity": "sha512-Nd3rgLBJcZ4iw7tpuOhwBupG6SvUDU0Fy1cZGAMorA2JmDUb+29Dg5phJK9gapa2Ak9d15w/RuMl/viwX+nKwQ==", + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "detect-indent": "^7.0.1", - "detect-newline": "^4.0.0", - "git-hooks-list": "^3.0.0", - "globby": "^13.1.2", - "is-plain-obj": "^4.1.0", - "sort-object-keys": "^1.1.3" + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" }, "bin": { - "sort-package-json": "cli.js" - } - }, - "node_modules/sort-package-json/node_modules/detect-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", - "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "jest": "bin/jest.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sort-package-json/node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/spdx-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", - "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "array-find-index": "^1.0.2", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/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==", - "dev": true - }, - "node_modules/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==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "node_modules/spdx-license-list": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.6.0.tgz", - "integrity": "sha512-vLwdf9AWgdJQmG8cai2HKfkInFsliKaCCOwXmdVonClIhdURTX61KdDOoXC1qcQ7gDaZj+CUTcrMJeAdnCtrKA==", - "dev": true, + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/spdx-ranges": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", - "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", - "dev": true - }, - "node_modules/spdx-satisfies": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", - "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { - "spdx-compare": "^1.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { - "readable-stream": "^3.0.0" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { - "escape-string-regexp": "^2.0.0" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/stack-utils/node_modules/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==", + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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==", + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "safe-buffer": "~5.2.0" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { - "node": ">=0.6.19" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/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==", + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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==", + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { - "min-indent": "^1.0.0" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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==", + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=8" - } - }, - "node_modules/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==", - "dev": true, - "engines": { - "node": ">=0.10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { - "readable-stream": "3" + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/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==", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=8.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/treeify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", - "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/jose": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", + "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", + "funding": { + "url": "https://github.com/sponsors/panva" } }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "dev": true, "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "node": ">=10" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" + "argparse": "^2.0.1" }, "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=0.4.0" + "node": ">=4" } }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "node_modules/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==", "dev": true }, - "node_modules/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, - "engines": { - "node": ">=4" - } + "node_modules/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==" }, - "node_modules/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, - "engines": { - "node": ">=8" + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" } }, - "node_modules/typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "node_modules/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==" + }, + "node_modules/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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=4.2.0" + "node": ">=6" } }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" }, "bin": { - "browserslist-lint": "cli.js" + "JSONStream": "bin.js" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "engines": { + "node": "*" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "dependencies": { - "punycode": "^2.1.0" + "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": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/util-extend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", - "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } }, - "node_modules/v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" }, "engines": { - "node": ">=10.12.0" + "node": ">=14" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } }, - "node_modules/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==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "json-buffer": "3.0.1" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/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==", "dev": true, - "dependencies": { - "makeerror": "1.0.12" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/knex": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", + "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", "dependencies": { - "isexe": "^2.0.0" + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.1", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" }, "bin": { - "node-which": "bin/node-which" + "knex": "bin/cli.js" }, "engines": { - "node": ">= 8" + "node": ">=12" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } } }, - "node_modules/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==", + "node_modules/knex/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/knex/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/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, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/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, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 0.8.0" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/libphonenumber-js": { + "version": "1.10.53", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.53.tgz", + "integrity": "sha512-sDTnnqlWK4vH4AlDQuswz3n4Hx7bIQWTpIcScJX+Sp7St3LXHmfiax/ZFfyYxHmkdCvydOLSuvtAO/XpXiSySw==" + }, + "node_modules/license-checker": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", + "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "chalk": "^2.4.1", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "read-installed": "~4.0.3", + "semver": "^5.5.0", + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-satisfies": "^4.0.0", + "treeify": "^1.1.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "bin": { + "license-checker": "bin/license-checker" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/license-checker/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, - "node_modules/wrap-ansi-cjs/node_modules/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==", + "node_modules/license-checker/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=4" } }, - "node_modules/wrap-ansi-cjs/node_modules/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 - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/license-checker/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "color-name": "1.1.3" } }, - "node_modules/wrap-ansi/node_modules/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==", + "node_modules/license-checker/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/license-checker/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "ms": "^2.1.1" } }, - "node_modules/wrap-ansi/node_modules/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 - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "node_modules/license-checker/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.8.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/license-checker/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz", - "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==", + "node_modules/license-checker/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "engines": { - "node": ">= 14", - "npm": ">= 7" + "bin": { + "semver": "bin/semver" } }, - "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "node_modules/license-checker/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" + "node_modules/light-my-request": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.9.1.tgz", + "integrity": "sha512-UT7pUk8jNCR1wR7w3iWfIjx32DiB2f3hFdQSOwy3/EPQ3n3VocyipUxcyRZR0ahoev+fky69uA+GejPa9KuHKg==", + "dependencies": { + "cookie": "^0.5.0", + "process-warning": "^2.0.0", + "set-cookie-parser": "^2.4.1" } }, - "node_modules/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==", + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "dev": true, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" } }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "requires": { - "@babel/highlight": "^7.18.6" - } + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, - "@babel/compat-data": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", - "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", - "dev": true + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, + "node_modules/linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "dev": true, "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "uc.micro": "^1.0.1" } }, - "@babel/generator": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", - "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" + "node_modules/lint-staged": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz", + "integrity": "sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", + "lilconfig": "2.1.0", + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" } }, - "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "node_modules/lint-staged/node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "node_modules/lint-staged/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.6" + "engines": { + "node": ">=14.18.0" } }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "requires": { - "@babel/types": "^7.18.6" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, - "requires": { - "@babel/types": "^7.20.2" + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.6" + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", - "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/parser": { - "version": "7.20.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", - "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", - "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==", + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@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==", + "node_modules/listr2": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } } }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "engines": { + "node": ">=6.11.5" } }, - "@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==", + "node_modules/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": { - "@babel/helper-plugin-utils": "^7.10.4" + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@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" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, - "@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" - } + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, - "@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" - } + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, - "@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" - } + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "@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" - } + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true }, - "@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" - } + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, - "@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" - } + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, - "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, - "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - } + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==" }, - "@babel/traverse": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", - "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.13", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", - "globals": "^11.1.0" - } + "node_modules/lodash.lowercase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz", + "integrity": "sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA==" }, - "@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } + "node_modules/lodash.lowerfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz", + "integrity": "sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w==" }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", "dev": true }, - "@commitlint/cli": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.4.3.tgz", - "integrity": "sha512-IPTS7AZuBHgD0gl24El8HwuDM9zJN9JLa5KmZUQoFD1BQeGGdzAYJOnAr85CeJWpTDok0BGHDL0+4odnH0iTyA==", - "dev": true, - "requires": { - "@commitlint/format": "^17.4.0", - "@commitlint/lint": "^17.4.3", - "@commitlint/load": "^17.4.2", - "@commitlint/read": "^17.4.2", - "@commitlint/types": "^17.4.0", - "execa": "^5.0.0", - "lodash.isfunction": "^3.0.9", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", - "yargs": "^17.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 - } - } + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true }, - "@commitlint/config-conventional": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.4.3.tgz", - "integrity": "sha512-8EsY2iDw74hCk3hIQSg7/E0R8/KtPjnFPZVwmmHxcjhZjkSykmxysefICPDnbI3xgxfov0zwL1WKDHM8zglJdw==", - "dev": true, - "requires": { - "conventional-changelog-conventionalcommits": "^5.0.0" - } + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, - "@commitlint/config-validator": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.0.tgz", - "integrity": "sha512-Sa/+8KNpDXz4zT4bVbz2fpFjvgkPO6u2V2fP4TKgt6FjmOw2z3eEX859vtfeaTav/ukBw0/0jr+5ZTZp9zCBhA==", - "dev": true, - "requires": { - "@commitlint/types": "^17.4.0", - "ajv": "^8.11.0" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "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 - } - } + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true }, - "@commitlint/ensure": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.4.0.tgz", - "integrity": "sha512-7oAxt25je0jeQ/E0O/M8L3ADb1Cvweu/5lc/kYF8g/kXatI0wxGE5La52onnAUAWeWlsuvBNar15WcrmDmr5Mw==", - "dev": true, - "requires": { - "@commitlint/types": "^17.4.0", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" - } + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "@commitlint/execute-rule": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", - "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", - "dev": true + "node_modules/lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==" }, - "@commitlint/format": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.0.tgz", - "integrity": "sha512-Z2bWAU5+f1YZh9W76c84J8iLIWIvvm+mzqogTz0Nsc1x6EHW0Z2gI38g5HAjB0r0I3ZjR15IDEJKhsxyblcyhA==", - "dev": true, - "requires": { - "@commitlint/types": "^17.4.0", - "chalk": "^4.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==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } - } + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" }, - "@commitlint/is-ignored": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.4.2.tgz", - "integrity": "sha512-1b2Y2qJ6n7bHG9K6h8S4lBGUl6kc7mMhJN9gy1SQfUZqe92ToDjUTtgNWb6LbzR1X8Cq4SEus4VU8Z/riEa94Q==", - "dev": true, - "requires": { - "@commitlint/types": "^17.4.0", - "semver": "7.3.8" - }, - "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==" }, - "@commitlint/lint": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.4.3.tgz", - "integrity": "sha512-GnPsqEYmXIB/MaBhRMzkiDJWyjuLrKad4xoxKO4N6Kc19iqjR4DPc/bl2dxeW9kUmtrAtefOzIEzJAevpA5y2w==", - "dev": true, - "requires": { - "@commitlint/is-ignored": "^17.4.2", - "@commitlint/parse": "^17.4.2", - "@commitlint/rules": "^17.4.3", - "@commitlint/types": "^17.4.0" - } + "node_modules/lodash.repeat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", + "integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==" }, - "@commitlint/load": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.4.2.tgz", - "integrity": "sha512-Si++F85rJ9t4hw6JcOw1i2h0fdpdFQt0YKwjuK4bk9KhFjyFkRxvR3SB2dPaMs+EwWlDrDBGL+ygip1QD6gmPw==", - "dev": true, - "requires": { - "@commitlint/config-validator": "^17.4.0", - "@commitlint/execute-rule": "^17.4.0", - "@commitlint/resolve-extends": "^17.4.0", - "@commitlint/types": "^17.4.0", - "@types/node": "*", - "chalk": "^4.1.0", - "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.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" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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 - }, - "cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", - "dev": true, - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^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 - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "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 - }, - "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" - } - } - } + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==" + }, + "node_modules/lodash.trim": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", + "integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==" + }, + "node_modules/lodash.trimend": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz", + "integrity": "sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA==" + }, + "node_modules/lodash.trimstart": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz", + "integrity": "sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ==" }, - "@commitlint/message": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", - "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, - "@commitlint/parse": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.4.2.tgz", - "integrity": "sha512-DK4EwqhxfXpyCA+UH8TBRIAXAfmmX4q9QRBz/2h9F9sI91yt6mltTrL6TKURMcjUVmgaB80wgS9QybNIyVBIJA==", - "dev": true, - "requires": { - "@commitlint/types": "^17.4.0", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "node_modules/lodash.uppercase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz", + "integrity": "sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA==" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==" + }, + "node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" } }, - "@commitlint/read": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.4.2.tgz", - "integrity": "sha512-hasYOdbhEg+W4hi0InmXHxtD/1favB4WdwyFxs1eOy/DvMw6+2IZBmATgGOlqhahsypk4kChhxjAFJAZ2F+JBg==", - "dev": true, - "requires": { - "@commitlint/top-level": "^17.4.0", - "@commitlint/types": "^17.4.0", - "fs-extra": "^11.0.0", - "git-raw-commits": "^2.0.0", - "minimist": "^1.2.6" + "node_modules/log-symbols/node_modules/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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "@commitlint/resolve-extends": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.0.tgz", - "integrity": "sha512-3JsmwkrCzoK8sO22AzLBvNEvC1Pmdn/65RKXzEtQMy6oYMl0Snrq97a5bQQEFETF0VsvbtUuKttLqqgn99OXRQ==", - "dev": true, - "requires": { - "@commitlint/config-validator": "^17.4.0", - "@commitlint/types": "^17.4.0", - "import-fresh": "^3.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" + "node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/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==", "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 - } + "color-name": "1.1.3" } }, - "@commitlint/rules": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.4.3.tgz", - "integrity": "sha512-xHReDfE3Z+O9p1sXeEhPRSk4FifBsC4EbXzvQ4aa0ykQe+n/iZDd4CrFC/Oiv2K9BU4ZnFHak30IbMLa4ks1Rw==", - "dev": true, - "requires": { - "@commitlint/ensure": "^17.4.0", - "@commitlint/message": "^17.4.2", - "@commitlint/to-lines": "^17.4.0", - "@commitlint/types": "^17.4.0", - "execa": "^5.0.0" + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/log-symbols/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" } }, - "@commitlint/to-lines": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", - "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", - "dev": true + "node_modules/log-symbols/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } }, - "@commitlint/top-level": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", - "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", - "dev": true, - "requires": { - "find-up": "^5.0.0" - }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "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" - } - } + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@commitlint/types": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.0.tgz", - "integrity": "sha512-2NjAnq5IcxY9kXtUeO2Ac0aPpvkuOmwbH/BxIm36XXK5LtWFObWJWjXOA+kcaABMrthjWu6la+FUpyYFMHRvbA==", + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, - "requires": { - "chalk": "^4.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==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/longest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", + "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } - } + "yallist": "^3.0.2" } }, - "@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" - }, + "node_modules/lru-memoizer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", + "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", "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 - } + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" } }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, - "@jest/console": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.4.2.tgz", - "integrity": "sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg==", + "node_modules/macos-release": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", + "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2", - "slash": "^3.0.0" + "engines": { + "node": ">=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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@jest/core": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.4.2.tgz", - "integrity": "sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ==", + "node_modules/magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", "dev": true, - "requires": { - "@jest/console": "^29.4.2", - "@jest/reporters": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.4.2", - "jest-config": "^29.4.2", - "jest-haste-map": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-resolve-dependencies": "^29.4.2", - "jest-runner": "^29.4.2", - "jest-runtime": "^29.4.2", - "jest-snapshot": "^29.4.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "jest-watcher": "^29.4.2", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.2", - "slash": "^3.0.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==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" } }, - "@jest/environment": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.4.2.tgz", - "integrity": "sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "requires": { - "@jest/fake-timers": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "jest-mock": "^29.4.2" + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@jest/expect": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.4.2.tgz", - "integrity": "sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ==", - "dev": true, - "requires": { - "expect": "^29.4.2", - "jest-snapshot": "^29.4.2" - } + "node_modules/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 }, - "@jest/expect-utils": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.2.tgz", - "integrity": "sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "requires": { - "jest-get-type": "^29.4.2" + "dependencies": { + "tmpl": "1.0.5" } }, - "@jest/fake-timers": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.4.2.tgz", - "integrity": "sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ==", + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.4.2", - "jest-mock": "^29.4.2", - "jest-util": "^29.4.2" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@jest/globals": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.4.2.tgz", - "integrity": "sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg==", + "node_modules/markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", "dev": true, - "requires": { - "@jest/environment": "^29.4.2", - "@jest/expect": "^29.4.2", - "@jest/types": "^29.4.2", - "jest-mock": "^29.4.2" + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" } }, - "@jest/reporters": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.4.2.tgz", - "integrity": "sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw==", + "node_modules/markdownlint": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.28.2.tgz", + "integrity": "sha512-yYaQXoKKPV1zgrFsyAuZPEQoe+JrY9GDag9ObKpk09twx4OCU5lut+0/kZPrQ3W7w82SmgKhd7D8m34aG1unVw==", "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2", - "jest-worker": "^29.4.2", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "markdown-it": "13.0.1", + "markdownlint-micromark": "0.1.2" + }, + "engines": { + "node": ">=14.18.0" } }, - "@jest/schemas": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.2.tgz", - "integrity": "sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g==", + "node_modules/markdownlint-cli": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.34.0.tgz", + "integrity": "sha512-4G9I++VBTZkaye6Yfc/7dU6HQHcyldZEVB+bYyQJLcpJOHKk/q5ZpGqK80oKMIdlxzsA3aWOJLZ4DkoaoUWXbQ==", "dev": true, - "requires": { - "@sinclair/typebox": "^0.25.16" + "dependencies": { + "commander": "~10.0.1", + "get-stdin": "~9.0.0", + "glob": "~10.2.2", + "ignore": "~5.2.4", + "js-yaml": "^4.1.0", + "jsonc-parser": "~3.2.0", + "markdownlint": "~0.28.2", + "minimatch": "~9.0.0", + "run-con": "~1.2.11" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=14" } }, - "@jest/source-map": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.2.tgz", - "integrity": "sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q==", + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "engines": { + "node": ">=14" } }, - "@jest/test-result": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.4.2.tgz", - "integrity": "sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA==", + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", + "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", "dev": true, - "requires": { - "@jest/console": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "@jest/test-sequencer": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz", - "integrity": "sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg==", + "node_modules/markdownlint-cli/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, - "requires": { - "@jest/test-result": "^29.4.2", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "slash": "^3.0.0" + "engines": { + "node": ">= 4" } }, - "@jest/transform": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.4.2.tgz", - "integrity": "sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ==", + "node_modules/markdownlint-micromark": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", + "integrity": "sha512-jRxlQg8KpOfM2IbCL9RXM8ZiYWz2rv6DlZAnGv8ASJQpUh6byTBnEsbuMZ6T2/uIgntyf7SKg/mEaEBo1164fQ==", "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.4.2", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-util": "^29.4.2", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "engines": { + "node": ">=14.18.0" } }, - "@jest/types": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.2.tgz", - "integrity": "sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==", + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, - "requires": { - "@jest/schemas": "^29.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" } }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" } }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, - "optional": true - }, - "@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" + "dependencies": { + "@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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@sinclair/typebox": { - "version": "0.25.21", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", - "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==", - "dev": true - }, - "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "node_modules/meow/node_modules/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==", "dev": true, - "requires": { - "type-detect": "4.0.8" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0" + "engines": { + "node": ">=10" } }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", "dev": true }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "node_modules/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==" }, - "@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "engines": { + "node": ">= 8" } }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" } }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "devOptional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" } }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "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": "*" + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" } }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/node": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", - "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "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/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "node_modules/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==", "dev": true, - "requires": { - "@types/yargs-parser": "*" + "engines": { + "node": ">=4" } }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", - "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/type-utils": "6.12.0", - "@typescript-eslint/utils": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "@typescript-eslint/parser": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", - "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "peer": true, - "requires": { - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/typescript-estree": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@typescript-eslint/scope-manager": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", - "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, - "requires": { - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0" + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" } }, - "@typescript-eslint/type-utils": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", - "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", + "node_modules/minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "6.12.0", - "@typescript-eslint/utils": "6.12.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "@typescript-eslint/types": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", - "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", - "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dependencies": { - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", - "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/typescript-estree": "6.12.0", - "semver": "^7.5.4" + "minipass": "^3.0.0", + "yallist": "^4.0.0" }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "@typescript-eslint/visitor-keys": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", - "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.12.0", - "eslint-visitor-keys": "^3.4.1" + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "requires": {} + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "node_modules/mnemonist": { + "version": "0.39.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.5.tgz", + "integrity": "sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==", + "dependencies": { + "obliterator": "^2.0.1" } }, - "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-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, + "node_modules/mongodb": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true } } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "node_modules/mongodb-connection-string-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } }, - "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" + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" } }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" } }, - "apisauce": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/apisauce/-/apisauce-2.1.5.tgz", - "integrity": "sha512-bkMlz0ZUnyS8vDigej9UBYo5dne9/bQrkgIiIkGaiDHF6e5OxhYRLJDYu65V/Ox86tmWVwepIntAoTmk4Db0Hg==", - "requires": { - "axios": "^0.21.4" + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" } }, - "app-module-path": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", - "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/mylas": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.13.tgz", + "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" } }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "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==", + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "node_modules/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==", "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 + "node_modules/nest-mongo-auth0": { + "resolved": "nest-mongo-auth0", + "link": true }, - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + "node_modules/nest-mongo-jwt": { + "resolved": "nest-mongo-jwt", + "link": true }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } + "node_modules/nest-pg-auth0": { + "resolved": "nest-pg-auth0", + "link": true }, - "babel-jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.4.2.tgz", - "integrity": "sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ==", - "dev": true, - "requires": { - "@jest/transform": "^29.4.2", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.4.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^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" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } - } + "node_modules/nest-pg-jwt": { + "resolved": "nest-pg-jwt", + "link": true }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "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": "^5.0.4", - "test-exclude": "^6.0.0" - } + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true }, - "babel-plugin-jest-hoist": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz", - "integrity": "sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "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" + "dependencies": { + "lodash": "^4.17.21" } }, - "babel-preset-jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz", - "integrity": "sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.4.2", - "babel-preset-current-node-syntax": "^1.0.0" + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "node_modules/nodemon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", + "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", "dev": true, - "requires": { - "big-integer": "^1.6.44" + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" } }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "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==", + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "engines": { + "node": ">=4" } }, - "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "node-int64": "^0.4.0" + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, - "requires": { - "run-applescript": "^5.0.0" + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" } }, - "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==", - "dev": true + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } }, - "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==", + "node_modules/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, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "engines": { + "node": ">=0.10.0" } }, - "caniuse-lite": { - "version": "1.0.30001452", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", - "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==", + "node_modules/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==", "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" + "node_modules/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==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "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 + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" } }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==" + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } }, - "cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" + "node_modules/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==", + "engines": { + "node": ">= 0.8" } }, - "cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", + "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==", "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true - }, - "slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - } - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=8" } }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "node_modules/ora/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + "node_modules/ora/node_modules/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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true + "node_modules/ora/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } }, - "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 + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } }, - "color-convert": { + "node_modules/ora/node_modules/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": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/ora/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true - }, - "commitlint": { - "version": "17.4.3", - "resolved": "https://registry.npmjs.org/commitlint/-/commitlint-17.4.3.tgz", - "integrity": "sha512-3MGkngRG3x3KY5uKWxgyKK7WU5apelorn4jeJsu8aCotuaoPXYtZX8Ym7a/ZzB19UUuWADnKWVTWBePvweu3aA==", - "dev": true, - "requires": { - "@commitlint/cli": "^17.4.3", - "@commitlint/types": "^17.4.0" - } - }, - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "node_modules/ora/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "node_modules/ora/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" } }, - "conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, - "conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "node_modules/ora/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, + "node_modules/ora/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - } + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true, - "requires": {} - }, - "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 - }, - "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" + "engines": { + "node": ">=0.10.0" } }, - "dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", "dev": true, - "requires": { - "ms": "2.1.2" + "dependencies": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, - "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": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - } + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "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==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", - "dev": true - }, - "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "dependencies": { - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - } + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" } }, - "default-browser-id": { + "node_modules/p-is-promise": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "engines": { + "node": ">=8" } }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "requires": { - "clone": "^1.0.2" + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "devOptional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true - }, - "detect-indent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", - "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", - "dev": true + "node_modules/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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "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 + "node_modules/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==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "node_modules/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==", "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" + "engines": { + "node": ">=6" } }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "node_modules/p-wait-for": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", + "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "dependencies": { + "p-timeout": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "diff-sequences": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.2.tgz", - "integrity": "sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==", - "dev": true + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" + "node_modules/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==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "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" + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, - "requires": { - "is-obj": "^2.0.0" + "engines": { + "node": ">=0.10.0" } }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "ejs": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", - "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", - "requires": { - "jake": "^10.6.1" + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" } }, - "electron-to-chromium": { - "version": "1.4.295", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz", - "integrity": "sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "peer": true, + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } }, - "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==" + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^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==", - "requires": { - "ansi-colors": "^4.1.1" + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" } }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true + "node_modules/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, + "engines": { + "node": ">=8" + } }, - "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" + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" } }, - "escalade": { + "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, - "eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, - "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.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" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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 - }, - "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==", - "dev": true - }, - "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" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.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 - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "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" - } - }, - "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" - } - }, - "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" - } - }, - "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" - } - }, - "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 - }, - "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-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "eslint-config-prettier": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", - "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "engines": { + "node": "14 || >=16.14" } }, - "eslint-plugin-security": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", - "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", - "dev": true, - "requires": { - "safe-regex": "^2.1.1" + "node_modules/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==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" } }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", + "peer": true + }, + "node_modules/peek-stream": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", + "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", + "dependencies": { + "buffer-from": "^1.0.0", + "duplexify": "^3.5.0", + "through2": "^2.0.3" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, + "node_modules/peek-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } + "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" } }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true + "node_modules/peek-stream/node_modules/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==" }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "node_modules/peek-stream/node_modules/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==", + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/peek-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, - "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" + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": 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" - } + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "node_modules/pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "node_modules/pg-error-enum": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/pg-error-enum/-/pg-error-enum-0.6.1.tgz", + "integrity": "sha512-EbQqez4NPbrKbx+WiqcqKbWBOMe6l9r6WypgMGjHXxUskHQ07RYAxttPVrp1vm6Ba5Nv9pa1rE6FBC4CBKj07A==" }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "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" + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.2.tgz", - "integrity": "sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ==", + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", "dev": true, - "requires": { - "@jest/expect-utils": "^29.4.2", - "jest-get-type": "^29.4.2", - "jest-matcher-utils": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2" + "engines": { + "node": ">=4" } }, - "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 + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } }, - "fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/pg-types": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz", + "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==", "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.0.1", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" } }, - "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 + "node_modules/pg/node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "node_modules/pg/node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" + "node_modules/pg/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" } }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" + "node_modules/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" } }, - "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" + "node_modules/pg/node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" } }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "requires": { - "minimatch": "^5.0.1" - }, + "node_modules/pg/node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "dependencies": { - "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "requires": { - "brace-expansion": "^2.0.1" - } - } + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "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==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" } }, - "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" + "node_modules/pgpass/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" } }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/pgtools": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pgtools/-/pgtools-1.0.1.tgz", + "integrity": "sha512-an+ztelmf7MAaSuQhpuwRlwdgOB4mlkQ9Kv3Vh3Ij9fnJqF3pG8defU+gM3Ig4x9LsFC1eV0vHaEFkxvOQjVKQ==", "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "pg": "^8.9.0", + "pg-connection-string": "^2.5.0", + "yargs": "^17.7.1" + }, + "bin": { + "createdbjs": "src/bin/createdb.js", + "dropdbjs": "src/bin/dropdb.js" } }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" - }, - "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "engines": { + "node": ">=8.6" }, - "dependencies": { - "signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "bin": { + "pidtree": "bin/pidtree.js" }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } + "engines": { + "node": ">=0.10" } }, - "fs-jetpack": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz", - "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==", - "requires": { - "minimatch": "^3.0.2", - "rimraf": "^2.6.3" + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "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==", - "dev": true + "engines": { + "node": ">=0.10.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 + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "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==", - "dev": true + "node_modules/pino": { + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", + "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.1.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } }, - "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 + "node_modules/pino-abstract-transport": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", + "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } }, - "get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", - "dev": true + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } }, - "git-hooks-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.0.0.tgz", - "integrity": "sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==", - "dev": true + "node_modules/pino-abstract-transport/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } }, - "git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "node_modules/pino-pretty": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-9.4.1.tgz", + "integrity": "sha512-loWr5SNawVycvY//hamIzyz3Fh5OSpvkcO13MwdDW+eKIGylobPLqnVGTDwDXkdmpJd1BhEG+qhDw09h6SqJiQ==", "dev": true, - "requires": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^4.0.1", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" } }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "node_modules/pino-pretty/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/pino-pretty/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" + }, + "node_modules/pino/node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, - "requires": { - "ini": "^1.3.4" + "engines": { + "node": ">= 6" } }, - "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 - }, - "globby": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", - "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "node_modules/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==", "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, "dependencies": { - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - } + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "gluegun": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/gluegun/-/gluegun-5.1.2.tgz", - "integrity": "sha512-Cwx/8S8Z4YQg07a6AFsaGnnnmd8mN17414NcPS3OoDtZRwxgsvwRNJNg69niD6fDa8oNwslCG0xH7rEpRNNE/g==", - "requires": { - "apisauce": "^2.1.5", - "app-module-path": "^2.2.0", - "cli-table3": "0.6.0", - "colors": "1.4.0", - "cosmiconfig": "7.0.1", - "cross-spawn": "7.0.3", - "ejs": "3.1.6", - "enquirer": "2.3.6", - "execa": "5.1.1", - "fs-jetpack": "4.3.1", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.lowercase": "^4.3.0", - "lodash.lowerfirst": "^4.3.1", - "lodash.pad": "^4.5.1", - "lodash.padend": "^4.6.1", - "lodash.padstart": "^4.6.1", - "lodash.repeat": "^4.1.0", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.trim": "^4.5.1", - "lodash.trimend": "^4.5.1", - "lodash.trimstart": "^4.5.1", - "lodash.uppercase": "^4.3.0", - "lodash.upperfirst": "^4.3.1", - "ora": "4.0.2", - "pluralize": "^8.0.0", - "semver": "7.3.5", - "which": "2.0.2", - "yargs-parser": "^21.0.0" + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } }, - "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==", - "dev": true + "node_modules/plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-array": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "engines": { + "node": ">=12" } }, - "has-flag": { + "node_modules/postgres-bytea": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dev": true, + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "node_modules/postgres-date": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz", + "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==", + "dev": true, + "engines": { + "node": ">=12" + } }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-range": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz", + "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==", "dev": true }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + "node_modules/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, + "engines": { + "node": ">= 0.8.0" + } }, - "husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true + "node_modules/prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/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==" + }, + "node_modules/process-warning": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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==" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/pumpify/node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "imurmurhash": { + "node_modules/query-types": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "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==", - "dev": true + "resolved": "https://registry.npmjs.org/query-types/-/query-types-0.1.4.tgz", + "integrity": "sha512-DvOCeSGI+RJeZ8+13NYTZUGbGva3P85GmATpY2ojj0mIsNfw70xeTpQW51JaYDZyQ2Li/svGT+FUdd4CBUoDcQ==" }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "node_modules/queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", + "dev": true, + "engines": { + "node": ">=12" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "requires": { - "has": "^1.0.3" + "dependencies": { + "safe-buffer": "^5.1.0" } }, - "is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true + "node_modules/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==", + "engines": { + "node": ">= 0.6" + } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, - "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==" + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } }, - "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==", + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "is-glob": { + "node_modules/read-installed": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "dependencies": { + "debuglog": "^1.0.1", + "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" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.2" } }, - "is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "node_modules/read-installed/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "requires": { - "is-docker": "^3.0.0" + "bin": { + "semver": "bin/semver" } }, - "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==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "dev": true, + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "node_modules/read-package-json/node_modules/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==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.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": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "node_modules/read-package-json/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "requires": { - "text-extensions": "^1.0.0" + "bin": { + "semver": "bin/semver" } }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/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": { - "is-docker": "^2.0.0" - }, "dependencies": { - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - } + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" } }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true + "node_modules/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, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/read-pkg-up/node_modules/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": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/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, "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 - } + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/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, "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" - } - } + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/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, + "engines": { + "node": ">=8" } }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/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==", "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "bin": { + "semver": "bin/semver" } }, - "jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "node_modules/read-pkg/node_modules/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, - "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" + "engines": { + "node": ">=8" } }, - "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.2.tgz", - "integrity": "sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg==", + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, - "requires": { - "@jest/core": "^29.4.2", - "@jest/types": "^29.4.2", - "import-local": "^3.0.2", - "jest-cli": "^29.4.2" + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" } }, - "jest-changed-files": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.4.2.tgz", - "integrity": "sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, "dependencies": { - "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" - } - } + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "jest-circus": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.4.2.tgz", - "integrity": "sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg==", - "dev": true, - "requires": { - "@jest/environment": "^29.4.2", - "@jest/expect": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.4.2", - "jest-matcher-utils": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-runtime": "^29.4.2", - "jest-snapshot": "^29.4.2", - "jest-util": "^29.4.2", - "p-limit": "^3.1.0", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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 - }, - "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" - } - }, - "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" - } - } + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" } }, - "jest-cli": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.4.2.tgz", - "integrity": "sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q==", - "dev": true, - "requires": { - "@jest/core": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/types": "^29.4.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.4.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "jest-config": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.4.2.tgz", - "integrity": "sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA==", + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.4.2", - "@jest/types": "^29.4.2", - "babel-jest": "^29.4.2", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.4.2", - "jest-environment-node": "^29.4.2", - "jest-get-type": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-runner": "^29.4.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "jest-diff": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.2.tgz", - "integrity": "sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g==", + "node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.2", - "jest-get-type": "^29.4.2", - "pretty-format": "^29.4.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "bin": { + "regexp-tree": "bin/regexp-tree" } }, - "jest-docblock": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.2.tgz", - "integrity": "sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "requires": { - "detect-newline": "^3.0.0" + "engines": { + "node": ">=0.10.0" } }, - "jest-each": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.4.2.tgz", - "integrity": "sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA==", - "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.2", - "jest-util": "^29.4.2", - "pretty-format": "^29.4.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "node_modules/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==", + "engines": { + "node": ">=0.10.0" } }, - "jest-environment-node": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.4.2.tgz", - "integrity": "sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w==", - "dev": true, - "requires": { - "@jest/environment": "^29.4.2", - "@jest/fake-timers": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "jest-mock": "^29.4.2", - "jest-util": "^29.4.2" + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "jest-get-type": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.2.tgz", - "integrity": "sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg==", - "dev": true + "node_modules/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, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } }, - "jest-haste-map": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.4.2.tgz", - "integrity": "sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw==", + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.2", - "jest-util": "^29.4.2", - "jest-worker": "^29.4.2", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" } }, - "jest-leak-detector": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz", - "integrity": "sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA==", + "node_modules/resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", "dev": true, - "requires": { - "jest-get-type": "^29.4.2", - "pretty-format": "^29.4.2" + "dependencies": { + "global-dirs": "^0.1.1" + }, + "engines": { + "node": ">=8" } }, - "jest-matcher-utils": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz", - "integrity": "sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg==", + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.4.2", - "jest-get-type": "^29.4.2", - "pretty-format": "^29.4.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "engines": { + "node": ">=10" } }, - "jest-message-util": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.2.tgz", - "integrity": "sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g==", + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.4.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "engines": { + "node": ">=4" } }, - "jest-mock": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.4.2.tgz", - "integrity": "sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g==", - "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "@types/node": "*", - "jest-util": "^29.4.2" + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" }, - "jest-regex-util": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.2.tgz", - "integrity": "sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig==", - "dev": true + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "jest-resolve": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.4.2.tgz", - "integrity": "sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw==", + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.4.2", - "jest-validate": "^29.4.2", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^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" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" } }, - "jest-resolve-dependencies": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz", - "integrity": "sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg==", + "node_modules/roarr/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/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, - "requires": { - "jest-regex-util": "^29.4.2", - "jest-snapshot": "^29.4.2" + "engines": { + "node": ">=0.12.0" } }, - "jest-runner": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.4.2.tgz", - "integrity": "sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg==", + "node_modules/run-con": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.12.tgz", + "integrity": "sha512-5257ILMYIF4RztL9uoZ7V9Q97zHtNHn5bN3NobeAnzB1P3ASLgg8qocM2u+R18ttp+VEM78N2LK8XcNVtnSRrg==", "dev": true, - "requires": { - "@jest/console": "^29.4.2", - "@jest/environment": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.2", - "jest-environment-node": "^29.4.2", - "jest-haste-map": "^29.4.2", - "jest-leak-detector": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-runtime": "^29.4.2", - "jest-util": "^29.4.2", - "jest-watcher": "^29.4.2", - "jest-worker": "^29.4.2", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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 - }, - "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" - } - }, - "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" - } - } + "deep-extend": "^0.6.0", + "ini": "~3.0.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" } }, - "jest-runtime": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.4.2.tgz", - "integrity": "sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw==", + "node_modules/run-con/node_modules/ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", "dev": true, - "requires": { - "@jest/environment": "^29.4.2", - "@jest/fake-timers": "^29.4.2", - "@jest/globals": "^29.4.2", - "@jest/source-map": "^29.4.2", - "@jest/test-result": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-mock": "^29.4.2", - "jest-regex-util": "^29.4.2", - "jest-resolve": "^29.4.2", - "jest-snapshot": "^29.4.2", - "jest-util": "^29.4.2", - "semver": "^7.3.5", - "slash": "^3.0.0", - "strip-bom": "^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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "jest-snapshot": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.4.2.tgz", - "integrity": "sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.4.2", - "@jest/transform": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.4.2", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.4.2", - "jest-get-type": "^29.4.2", - "jest-haste-map": "^29.4.2", - "jest-matcher-utils": "^29.4.2", - "jest-message-util": "^29.4.2", - "jest-util": "^29.4.2", - "natural-compare": "^1.4.0", - "pretty-format": "^29.4.2", - "semver": "^7.3.5" - }, - "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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 + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "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 + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "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": "consulting", + "url": "https://feross.org/support" } + ], + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "jest-util": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.2.tgz", - "integrity": "sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==", - "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "tslib": "^2.1.0" } }, - "jest-validate": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.4.2.tgz", - "integrity": "sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q==", - "dev": true, - "requires": { - "@jest/types": "^29.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.2", - "leven": "^3.1.0", - "pretty-format": "^29.4.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" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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 + "node_modules/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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "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 + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "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": "consulting", + "url": "https://feross.org/support" } - } + ] }, - "jest-watcher": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.4.2.tgz", - "integrity": "sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w==", + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", "dev": true, - "requires": { - "@jest/test-result": "^29.4.2", - "@jest/types": "^29.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.4.2", - "string-length": "^4.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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" - } - } + "regexp-tree": "~0.1.1" } }, - "jest-worker": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.2.tgz", - "integrity": "sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.4.2", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, + "node_modules/safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", "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": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "ret": "~0.2.0" } }, - "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "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==" + "node_modules/schema-utils/node_modules/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, + "peerDependencies": { + "ajv": "^6.9.1" + } }, - "json-schema-traverse": { + "node_modules/schema-utils/node_modules/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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - }, "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" } }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "node_modules/seek-bzip/node_modules/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 }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "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==", - "dev": true + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "node_modules/semver/node_modules/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==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } }, - "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 + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "license-checker": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", - "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "read-installed": "~4.0.3", - "semver": "^5.5.0", - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-satisfies": "^4.0.0", - "treeify": "^1.1.0" + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "ms": "2.0.0" } }, - "lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "dev": true + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "linkify-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", - "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, - "requires": { - "uc.micro": "^1.0.1" + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lint-staged": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.2.tgz", - "integrity": "sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==", + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, - "requires": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.19", - "commander": "^9.4.1", - "debug": "^4.3.4", - "execa": "^6.1.0", - "lilconfig": "2.0.6", - "listr2": "^5.0.5", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.2", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.1.3" - }, - "dependencies": { - "execa": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", - "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^3.0.1", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", - "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "listr2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.7.tgz", - "integrity": "sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==", + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.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" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.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 - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } + "randombytes": "^2.1.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" + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + "node_modules/set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" }, - "lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==" + "node_modules/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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, - "lodash.lowercase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz", - "integrity": "sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA==" + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } }, - "lodash.lowerfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz", - "integrity": "sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w==" + "node_modules/shellcheck": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/shellcheck/-/shellcheck-2.2.0.tgz", + "integrity": "sha512-rMt0WhmeqRrKMUqyTlkL6pd0zY27FRQMQWjQhpHMQETwG2ykc8gz+QGGtxHym4R2np646QgQAcq04sAEo3SWhA==", + "dev": true, + "dependencies": { + "decompress": "^4.2.1", + "global-agent": "^3.0.0" + }, + "bin": { + "shellcheck": "bin/shellcheck.js" + }, + "engines": { + "node": ">=18.4.0 || >=16.17.0" + } }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true + "node_modules/shelljs/node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==" + "node_modules/shelljs/node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } }, - "lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==" + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "lodash.repeat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", - "integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==" + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true }, - "lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==" + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } }, - "lodash.trim": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", - "integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==" + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "lodash.trimend": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz", - "integrity": "sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA==" + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "dev": true, + "engines": { + "node": "*" + } }, - "lodash.trimstart": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz", - "integrity": "sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ==" + "node_modules/sonic-boom": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", + "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "node_modules/sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", "dev": true }, - "lodash.uppercase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz", - "integrity": "sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA==" + "node_modules/sort-package-json": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.6.0.tgz", + "integrity": "sha512-XSQ+lY9bAYA8ZsoChcEoPlgcSMaheziEp1beox1JVxy1SV4F2jSq9+h2rJ+3mC/Dhu9Ius1DLnInD5AWcsDXZw==", + "dev": true, + "dependencies": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "get-stdin": "^9.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "bin": { + "sort-package-json": "cli.js" + } }, - "lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==" + "node_modules/sort-package-json/node_modules/detect-newline": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz", + "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "requires": { - "chalk": "^2.4.2" + "node_modules/sort-package-json/node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "log-update": { + "node_modules/sort-package-json/node_modules/slash": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.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 - }, - "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==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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" + "node_modules/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, + "engines": { + "node": ">=0.10.0" } }, - "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==", + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "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 - } + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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==", "dev": true }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/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==", "dev": true, - "requires": { - "tmpl": "1.0.5" + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, - "markdown-it": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", - "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "node_modules/spdx-license-list": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.8.0.tgz", + "integrity": "sha512-5UdM7r9yJ1EvsPQZWfa41AZjLQngl9iMMysm9XBW7Lqhq7aF8cllfqjS+rFCHB8FFMGSM0yFWue2LUV9mR0QzQ==", "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "engines": { + "node": ">=8" }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "markdownlint": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.28.2.tgz", - "integrity": "sha512-yYaQXoKKPV1zgrFsyAuZPEQoe+JrY9GDag9ObKpk09twx4OCU5lut+0/kZPrQ3W7w82SmgKhd7D8m34aG1unVw==", + "node_modules/spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true + }, + "node_modules/spdx-satisfies": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", + "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", "dev": true, - "requires": { - "markdown-it": "13.0.1", - "markdownlint-micromark": "0.1.2" + "dependencies": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" } }, - "markdownlint-cli": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.34.0.tgz", - "integrity": "sha512-4G9I++VBTZkaye6Yfc/7dU6HQHcyldZEVB+bYyQJLcpJOHKk/q5ZpGqK80oKMIdlxzsA3aWOJLZ4DkoaoUWXbQ==", + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "dev": true, - "requires": { - "commander": "~10.0.1", - "get-stdin": "~9.0.0", - "glob": "~10.2.2", - "ignore": "~5.2.4", - "js-yaml": "^4.1.0", - "jsonc-parser": "~3.2.0", - "markdownlint": "~0.28.2", - "minimatch": "~9.0.0", - "run-con": "~1.2.11" - }, "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true - }, - "glob": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz", - "integrity": "sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } + "readable-stream": "^3.0.0" } }, - "markdownlint-micromark": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", - "integrity": "sha512-jRxlQg8KpOfM2IbCL9RXM8ZiYWz2rv6DlZAnGv8ASJQpUh6byTBnEsbuMZ6T2/uIgntyf7SKg/mEaEBo1164fQ==", - "dev": true - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "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": { - "hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "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==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "merge-stream": { + "node_modules/stack-utils/node_modules/escape-string-regexp": { "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==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "engines": { + "node": ">=8" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, - "min-indent": { + "node_modules/stream-shift": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - }, + "node_modules/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==", "dependencies": { - "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" - } - } + "safe-buffer": "~5.2.0" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", - "dev": true + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "minimist": "^1.2.6" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "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 + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "node_modules/string-width/node_modules/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==", + "engines": { + "node": ">=8" + } }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "node_modules/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, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": 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": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "is-natural-number": "^4.0.1" } }, - "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 - }, - "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==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" + "node_modules/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==", + "engines": { + "node": ">=6" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "node_modules/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==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" } }, - "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" + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" } }, - "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "node_modules/supertest": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", + "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", "dev": true, - "requires": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.0.5" + }, + "engines": { + "node": ">=6.4.0" } }, - "ora": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", - "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==", - "requires": { - "chalk": "^2.4.2", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.2.0", - "is-interactive": "^1.0.0", - "log-symbols": "^3.0.0", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1" - }, + "node_modules/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==", "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - }, - "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" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true + "node_modules/swagger-ui-dist": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.18.2.tgz", + "integrity": "sha512-oVBoBl9Dg+VJw8uRWDxlyUyHoNEDC0c1ysT6+Boy6CTgr2rUcLcfPon4RvxgS2/taNW6O0+US+Z/dlAsWFjOAQ==" }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "engines": { + "node": ">=0.10" } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, - "requires": { - "p-try": "^2.0.0" + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, - "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==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "requires": { - "p-limit": "^2.2.0" + "engines": { + "node": ">=6" } }, - "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==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "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==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "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" + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "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" } }, - "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 - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "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.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/tar-stream/node_modules/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==", "dev": true }, - "path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "node_modules/tar-stream/node_modules/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": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" - }, "dependencies": { - "lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", - "dev": true - } + "safe-buffer": "~5.1.0" } }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } }, - "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==", - "dev": true, - "requires": { - "find-up": "^4.0.0" + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, - "pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", - "dev": true + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "engines": { + "node": ">=8.0.0" + } }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "node_modules/terser": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", + "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", "dev": true, - "requires": { - "fast-diff": "^1.1.2" + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "pretty-format": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.2.tgz", - "integrity": "sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, - "requires": { - "@jest/schemas": "^29.4.2", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true } } }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "read-installed": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", - "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": 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" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "read-package-json": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", - "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", - "dev": true, - "requires": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } + "node_modules/terser/node_modules/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 }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "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 - } + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "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==", + "node_modules/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": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/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==", "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "engines": { + "node": ">=0.10" } }, - "regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "node_modules/thread-stream": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", + "dependencies": { + "real-require": "^0.2.0" + } }, - "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==", + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "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==", + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "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 - } + "readable-stream": "3" } }, - "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-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "dev": true, - "requires": { - "global-dirs": "^0.1.1" + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "engines": { + "node": ">=8" } }, - "resolve.exports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz", - "integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==", - "dev": true + "node_modules/tiny-lru": { + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.2.5.tgz", + "integrity": "sha512-JpqM0K33lG6iQGKiigcwuURAKZlq6rHXfrgeL4/I8/REoyJTGU+tEMszvT/oTRVHG2OiylhGDjqPp1jWMlr3bw==", + "engines": { + "node": ">=12" + } }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" } }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", "dev": true }, - "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" - } - }, - "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "requires": { - "execa": "^5.0.0" + "engines": { + "node": ">=4" } }, - "run-con": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz", - "integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==", + "node_modules/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==", "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~3.0.0", - "minimist": "^1.2.6", - "strip-json-comments": "~3.1.1" - }, "dependencies": { - "ini": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", - "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", - "dev": true - } + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, - "requires": { - "queue-microtask": "^1.2.2" + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" } }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "node_modules/touch/node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", "dev": true, - "requires": { - "tslib": "^2.1.0" + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" } }, - "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 + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "node_modules/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, - "requires": { - "regexp-tree": "~0.1.1" + "bin": { + "tree-kill": "cli.js" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "engines": { + "node": ">=0.6" } }, - "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" + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "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==", - "dev": true + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/ts-jest": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "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" - } + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true }, - "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" - } + "babel-jest": { + "optional": true }, - "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 + "esbuild": { + "optional": true } } }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", - "dev": true - }, - "sort-object-keys": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", - "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", - "dev": true - }, - "sort-package-json": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.4.1.tgz", - "integrity": "sha512-Nd3rgLBJcZ4iw7tpuOhwBupG6SvUDU0Fy1cZGAMorA2JmDUb+29Dg5phJK9gapa2Ak9d15w/RuMl/viwX+nKwQ==", + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, - "requires": { - "detect-indent": "^7.0.1", - "detect-newline": "^4.0.0", - "git-hooks-list": "^3.0.0", - "globby": "^13.1.2", - "is-plain-obj": "^4.1.0", - "sort-object-keys": "^1.1.3" - }, "dependencies": { - "detect-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", - "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", - "dev": true - }, - "is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true - } + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "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-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "engines": { + "node": ">= 8" } }, - "spdx-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", - "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "requires": { - "array-find-index": "^1.0.2", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "node_modules/tsc-alias": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.8.tgz", + "integrity": "sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==", "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" } }, - "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==", - "dev": true - }, - "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==", + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "engines": { + "node": "^12.20.0 || >=14" } }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "spdx-license-list": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.6.0.tgz", - "integrity": "sha512-vLwdf9AWgdJQmG8cai2HKfkInFsliKaCCOwXmdVonClIhdURTX61KdDOoXC1qcQ7gDaZj+CUTcrMJeAdnCtrKA==", - "dev": true - }, - "spdx-ranges": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", - "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", - "dev": true + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } }, - "spdx-satisfies": { + "node_modules/tsconfig-paths-webpack-plugin": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", - "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz", + "integrity": "sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==", "dev": true, - "requires": { - "spdx-compare": "^1.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" } }, - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "requires": { - "readable-stream": "^3.0.0" + "engines": { + "node": ">=4" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "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 - } - } - }, - "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" + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/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": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "node_modules/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, + "engines": { + "node": ">=4" } }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, - "requires": { - "ansi-regex": "^5.0.1" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "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==", + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "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==" + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } }, - "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==", + "node_modules/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==", "dev": true, - "requires": { - "min-indent": "^1.0.0" + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" } }, - "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==", + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" } }, - "supports-preserve-symlinks-flag": { + "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } }, - "synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "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" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" } }, - "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==", - "dev": true + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "node_modules/util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, - "requires": { - "readable-stream": "3" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" } }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "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": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.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==", + "node_modules/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==", "dev": true, - "requires": { - "is-number": "^7.0.0" + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "treeify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", - "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", - "dev": true + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } }, - "ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": {} + "dependencies": { + "makeerror": "1.0.12" + } }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, "dependencies": { - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - } + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" } }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "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 + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } }, - "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 + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", - "dev": true + "node_modules/webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "node_modules/webpack/node_modules/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": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "requires": { - "punycode": "^2.1.0" + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "util-extend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", - "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } }, - "v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "node_modules/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": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - } + "execa": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/windows-release/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/windows-release/node_modules/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==", "dev": true, - "requires": { - "makeerror": "1.0.12" + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "requires": { - "defaults": "^1.0.3" + "node_modules/windows-release/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.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" + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, - "wrap-ansi": { + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "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==", - "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 - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "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 - } + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "write-file-atomic": { + "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "dev": true, + "engines": { + "node": ">=10" + } }, - "yaml": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz", - "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -17343,24 +16856,96 @@ "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } }, - "yn": { + "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "yocto-queue": { + "node_modules/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 + "devOptional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-openapi": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/zod-openapi/-/zod-openapi-2.2.3.tgz", + "integrity": "sha512-WKkmSfEQh8lDT0ML+rn6TZjV2uZ3Hx72SZ/+dzU1uVgZaqqIsYTETHWaahXbCROKH2/wgwNdZhXC6FMpKAGmQw==", + "engines": { + "node": ">=16.11" + }, + "peerDependencies": { + "zod": "^3.21.4" + } + }, + "node-cli": { + "name": "@mentormate/node-cli", + "version": "3.2.0", + "license": "MIT", + "dependencies": { + "gluegun": "^5.1.2" + }, + "bin": { + "node-cli": "bin/node-cli" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.4.3", + "@typescript-eslint/eslint-plugin": "^6.12.0", + "commitlint": "^17.4.3", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-security": "^1.7.1", + "husky": "^8.0.3", + "jest": "^29.4.2", + "license-checker": "^25.0.1", + "lint-staged": "^13.1.2", + "markdownlint-cli": "^0.34.0", + "prettier": "^3.1.0", + "sort-package-json": "^2.4.1", + "spdx-license-list": "^6.6.0" + } } } } diff --git a/package.json b/package.json index d8577c16..46641be2 100644 --- a/package.json +++ b/package.json @@ -1,49 +1,18 @@ { - "name": "@mentormate/node-cli", + "name": "@mentormate/mono-node-cli", "version": "3.2.0", "description": "Node CLI", "repository": { "url": "https://github.com/MentorMate/node-project-cli" }, "license": "MIT", - "bin": { - "node-cli": "bin/node-cli" - }, - "files": [ - "assets", - "src", - "docs", - "bin" - ], - "scripts": { - "format": "prettier \"**/*.{js,md}\" --write --cache --cache-strategy metadata --cache-location .prettiercache", - "license:check": "license-checker --summary --onlyAllow $(node ./licenses-allowed.js ';') > /dev/null", - "license:for-review": "license-checker --summary --exclude $(node ./licenses-allowed.js ',')", - "license:summary": "license-checker --summary", - "lint": "eslint \"**/*.js\" --fix --cache", - "prepare": "husky install", - "test": "jest", - "test:cov": "jest --coverage", - "test:watch": "jest --watch" - }, - "dependencies": { - "gluegun": "^5.1.2" - }, - "devDependencies": { - "@commitlint/config-conventional": "^17.4.3", - "@typescript-eslint/eslint-plugin": "^6.12.0", - "commitlint": "^17.4.3", - "eslint": "^8.34.0", - "eslint-config-prettier": "^8.6.0", - "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-security": "^1.7.1", - "husky": "^8.0.3", - "jest": "^29.4.2", - "license-checker": "^25.0.1", - "lint-staged": "^13.1.2", - "markdownlint-cli": "^0.34.0", - "prettier": "^3.1.0", - "sort-package-json": "^2.4.1", - "spdx-license-list": "^6.6.0" - } + "workspaces": [ + "node-cli", + "nest-pg-auth0", + "nest-pg-jwt", + "nest-mongo-auth0", + "nest-mongo-jwt", + "express-pg-jwt", + "express-pg-auth0" + ] }