From 0d44c15923fe830b27c36516705801ede39217c8 Mon Sep 17 00:00:00 2001
From: FortiShield <161459699+FortiShield@users.noreply.github.com>
Date: Wed, 15 May 2024 06:09:20 +0600
Subject: [PATCH] Develop (#8)
* development init
* Update README.md
Signed-off-by: gitworkflows <118260833+gitworkflows@users.noreply.github.com>
---------
Signed-off-by: gitworkflows <118260833+gitworkflows@users.noreply.github.com>
Co-authored-by: gitworkflows <118260833+gitworkflows@users.noreply.github.com>
---
.all-contributorsrc | 50 +
.dockerignore | 4 +
.eslintrc.js | 25 +-
.github/ISSUE_TEMPLATE/bug_report.md | 31 +
.github/workflows/docker-e2e.yml | 41 +
.github/workflows/npm-ci.yml | 30 -
.gitignore | 6 +
.husky/commit-msg | 1 +
.husky/pre-commit | 2 +
.hygen.js | 3 +
.hygen/seeds/create-document/module.ejs.t | 21 +
.../create-document/run-seed-import.ejs.t | 6 +
.../create-document/run-seed-service.ejs.t | 6 +
.../create-document/seed-module-import.ejs.t | 6 +
.../seeds/create-document/seed-module.ejs.t | 6 +
.hygen/seeds/create-document/service.ejs.t | 24 +
.hygen/seeds/create-relational/module.ejs.t | 14 +
.../create-relational/run-seed-import.ejs.t | 6 +
.../create-relational/run-seed-service.ejs.t | 6 +
.../seed-module-import.ejs.t | 6 +
.../seeds/create-relational/seed-module.ejs.t | 6 +
.hygen/seeds/create-relational/service.ejs.t | 23 +
.install-scripts/helpers/replace.ts | 25 +
.install-scripts/index.ts | 81 +
.install-scripts/scripts/remove-auth-apple.ts | 55 +
.../scripts/remove-auth-facebook.ts | 60 +
.../scripts/remove-auth-google.ts | 55 +
.../scripts/remove-auth-twitter.ts | 63 +
.../scripts/remove-install-scripts.ts | 25 +
.install-scripts/scripts/remove-mongodb.ts | 340 +
.install-scripts/scripts/remove-postgresql.ts | 364 +
.nvmrc | 1 +
.vscode/extensions.json | 6 +
.vscode/settings.json | 5 +
CHANGELOG.md | 0
CODE_OF_CONDUCT.md | 128 +
Dockerfile | 22 +
LICENSE | 2 +-
Procfile | 2 +
README.md | 70 +-
commitlint.config.js | 3 +
docker-compose.document.ci.yaml | 30 +
docker-compose.document.test.yaml | 33 +
docker-compose.document.yaml | 45 +
docker-compose.relational.ci.yaml | 30 +
docker-compose.relational.test.yaml | 33 +
docker-compose.yaml | 41 +
docs/architecture.md | 43 +
docs/auth.md | 183 +
docs/automatic-update-dependencies.md | 7 +
docs/benchmarking.md | 17 +
docs/database.md | 298 +
docs/file-uploading.md | 183 +
docs/installing-and-running.md | 215 +
docs/introduction.md | 29 +
docs/readme.md | 16 +
docs/serialization.md | 94 +
docs/tests.md | 41 +
document.Dockerfile | 22 +
document.e2e.Dockerfile | 22 +
document.test.Dockerfile | 22 +
env-example-document | 55 +
env-example-relational | 63 +
maildev.Dockerfile | 5 +
nest-cli.json | 6 +-
package-lock.json | 9446 -----------------
package.json | 198 +-
relational.e2e.Dockerfile | 22 +
relational.test.Dockerfile | 22 +
renovate.json | 5 +
src/app.controller.spec.ts | 22 -
src/app.controller.ts | 12 -
src/app.module.ts | 102 +-
src/auth-apple/auth-apple.controller.ts | 39 +
src/auth-apple/auth-apple.module.ts | 13 +
src/auth-apple/auth-apple.service.ts | 26 +
src/auth-apple/config/apple-config.type.ts | 3 +
src/auth-apple/config/apple.config.ts | 19 +
src/auth-apple/dto/auth-apple-login.dto.ts | 16 +
src/auth-facebook/auth-facebook.controller.ts | 42 +
src/auth-facebook/auth-facebook.module.ts | 13 +
src/auth-facebook/auth-facebook.service.ts | 45 +
.../config/facebook-config.type.ts | 4 +
src/auth-facebook/config/facebook.config.ts | 24 +
.../dto/auth-facebook-login.dto.ts | 8 +
.../interfaces/facebook.interface.ts | 6 +
src/auth-google/auth-google.controller.ts | 39 +
src/auth-google/auth-google.module.ts | 13 +
src/auth-google/auth-google.service.ts | 51 +
src/auth-google/config/google-config.type.ts | 4 +
src/auth-google/config/google.config.ts | 24 +
src/auth-google/dto/auth-google-login.dto.ts | 8 +
src/auth-twitter/auth-twitter.controller.ts | 42 +
src/auth-twitter/auth-twitter.module.ts | 13 +
src/auth-twitter/auth-twitter.service.ts | 42 +
.../config/twitter-config.type.ts | 4 +
src/auth-twitter/config/twitter.config.ts | 22 +
.../dto/auth-twitter-login.dto.ts | 12 +
src/auth/auth-providers.enum.ts | 7 +
src/auth/auth.controller.ts | 152 +
src/auth/auth.module.ts | 25 +
src/auth/auth.service.ts | 625 ++
src/auth/config/auth-config.type.ts | 10 +
src/auth/config/auth.config.ts | 46 +
src/auth/dto/auth-confirm-email.dto.ts | 8 +
src/auth/dto/auth-email-login.dto.ts | 16 +
src/auth/dto/auth-forgot-password.dto.ts | 11 +
src/auth/dto/auth-register-login.dto.ts | 23 +
src/auth/dto/auth-reset-password.dto.ts | 12 +
src/auth/dto/auth-update.dto.ts | 39 +
src/auth/dto/login-response.dto.ts | 18 +
src/auth/dto/refresh-response.dto.ts | 12 +
src/auth/strategies/anonymous.strategy.ts | 14 +
src/auth/strategies/jwt-refresh.strategy.ts | 30 +
src/auth/strategies/jwt.strategy.ts | 27 +
src/auth/strategies/types/jwt-payload.type.ts | 8 +
.../types/jwt-refresh-payload.type.ts | 8 +
src/config/app-config.type.ts | 11 +
src/config/app.config.ts | 70 +
src/config/config.type.ts | 21 +
src/database/config/database-config.type.ts | 17 +
src/database/config/database.config.ts | 99 +
src/database/data-source.ts | 42 +
.../migrations/1715028537217-CreateUser.ts | 79 +
src/database/mongoose-config.service.ts | 21 +
src/database/seeds/document/run-seed.ts | 15 +
src/database/seeds/document/seed.module.ts | 24 +
.../seeds/document/user/user-seed.module.ts | 21 +
.../seeds/document/user/user-seed.service.ts | 64 +
.../seeds/relational/role/role-seed.module.ts | 12 +
.../relational/role/role-seed.service.ts | 45 +
src/database/seeds/relational/run-seed.ts | 18 +
src/database/seeds/relational/seed.module.ts | 31 +
.../relational/status/status-seed.module.ts | 11 +
.../relational/status/status-seed.service.ts | 30 +
.../seeds/relational/user/user-seed.module.ts | 12 +
.../relational/user/user-seed.service.ts | 78 +
src/database/typeorm-config.service.ts | 57 +
src/files/config/file-config.type.ts | 14 +
src/files/config/file.config.ts | 48 +
src/files/domain/file.ts | 56 +
src/files/dto/file.dto.ts | 11 +
src/files/files.module.ts | 33 +
src/files/files.service.ts | 15 +
.../document/document-persistence.module.ts | 21 +
.../document/entities/file.schema.ts | 64 +
.../document/mappers/file.mapper.ts | 19 +
.../document/repositories/file.repository.ts | 35 +
.../persistence/file.repository.ts | 11 +
.../relational/entities/file.entity.ts | 61 +
.../relational/mappers/file.mapper.ts | 18 +
.../relational-persistence.module.ts | 17 +
.../repositories/file.repository.ts | 35 +
.../uploader/local/dto/file-response.dto.ts | 9 +
.../uploader/local/files.controller.ts | 62 +
.../uploader/local/files.module.ts | 73 +
.../uploader/local/files.service.ts | 37 +
.../s3-presigned/dto/file-response.dto.ts | 14 +
.../uploader/s3-presigned/dto/file.dto.ts | 12 +
.../uploader/s3-presigned/files.controller.ts | 25 +
.../uploader/s3-presigned/files.module.ts | 89 +
.../uploader/s3-presigned/files.service.ts | 93 +
.../uploader/s3/dto/file-response.dto.ts | 9 +
.../uploader/s3/files.controller.ts | 52 +
.../uploader/s3/files.module.ts | 90 +
.../uploader/s3/files.service.ts | 29 +
src/home/home.controller.ts | 15 +
src/home/home.module.ts | 11 +
src/home/home.service.ts | 12 +
src/i18n/en/common.json | 4 +
src/i18n/en/confirm-email.json | 5 +
src/i18n/en/confirm-new-email.json | 5 +
src/i18n/en/reset-password.json | 6 +
src/mail/config/mail-config.type.ts | 11 +
src/mail/config/mail.config.ts | 63 +
src/mail/interfaces/mail-data.interface.ts | 4 +
src/mail/mail-templates/activation.hbs | 33 +
src/mail/mail-templates/confirm-new-email.hbs | 33 +
src/mail/mail-templates/reset-password.hbs | 38 +
src/mail/mail.module.ts | 11 +
src/mail/mail.service.ts | 169 +
src/mailer/mailer.module.ts | 8 +
src/mailer/mailer.service.ts | 53 +
src/main.ts | 51 +-
src/roles/domain/role.ts | 25 +
src/roles/dto/role.dto.ts | 9 +
.../document/entities/role.schema.ts | 14 +
.../relational/entities/role.entity.ts | 21 +
src/roles/roles.decorator.ts | 3 +
src/roles/roles.enum.ts | 4 +
src/roles/roles.guard.ts | 20 +
src/session/domain/session.ts | 10 +
.../document/document-persistence.module.ts | 21 +
.../document/entities/session.schema.ts | 34 +
.../document/mappers/session.mapper.ts | 35 +
.../repositories/session.repository.ts | 82 +
.../relational/entities/session.entity.ts | 39 +
.../relational/mappers/session.mapper.ts | 35 +
.../relational-persistence.module.ts | 17 +
.../repositories/session.repository.ts | 85 +
.../persistence/session.repository.ts | 30 +
src/session/session.module.ts | 21 +
src/session/session.service.ts | 39 +
src/social/interfaces/social.interface.ts | 6 +
src/social/tokens.ts | 12 +
src/statuses/domain/status.ts | 25 +
src/statuses/dto/status.dto.ts | 9 +
.../document/entities/status.schema.ts | 14 +
.../relational/entities/status.entity.ts | 22 +
src/statuses/statuses.enum.ts | 4 +
src/users/domain/user.ts | 83 +
src/users/dto/create-user.dto.ts | 47 +
src/users/dto/query-user.dto.ts | 61 +
src/users/dto/update-user.dto.ts | 50 +
.../document/document-persistence.module.ts | 21 +
.../document/entities/user.schema.ts | 126 +
.../document/mappers/user.mapper.ts | 85 +
.../document/repositories/user.repository.ts | 97 +
.../relational/entities/user.entity.ts | 128 +
.../relational/mappers/user.mapper.ts | 74 +
.../relational-persistence.module.ts | 17 +
.../repositories/user.repository.ts | 93 +
.../persistence/user.repository.ts | 32 +
src/users/users.controller.ts | 139 +
src/users/users.module.ts | 25 +
src/users/users.service.ts | 196 +
src/utils/deep-resolver.ts | 30 +
src/utils/document-entity-helper.ts | 22 +
.../domain-to-document-condition.spec.ts | 27 +
src/utils/domain-to-document-condition.ts | 73 +
.../dto/infinity-pagination-response.dto.ts | 27 +
src/utils/infinity-pagination.ts | 12 +
src/utils/relational-entity-helper.ts | 15 +
src/utils/serializer.interceptor.ts | 16 +
.../transformers/lower-case.transformer.ts | 6 +
src/utils/types/deep-partial.type.ts | 3 +
src/utils/types/entity-condition.type.ts | 3 +
src/utils/types/maybe.type.ts | 1 +
src/utils/types/nullable.type.ts | 1 +
src/utils/types/or-never.type.ts | 1 +
src/utils/types/pagination-options.ts | 4 +
src/utils/validate-config.ts | 22 +
src/utils/validation-options.ts | 33 +
startup.document.ci.sh | 10 +
startup.document.dev.sh | 7 +
startup.document.test.sh | 8 +
startup.relational.ci.sh | 11 +
startup.relational.dev.sh | 7 +
startup.relational.test.sh | 9 +
test/admin/auth.e2e-spec.ts | 20 +
test/admin/users.e2e-spec.ts | 148 +
test/app.e2e-spec.ts | 24 -
test/user/auth.e2e-spec.ts | 348 +
test/utils/constants.ts | 7 +
tsconfig.json | 11 +-
wait-for-it.sh | 182 +
256 files changed, 10366 insertions(+), 9609 deletions(-)
create mode 100644 .all-contributorsrc
create mode 100644 .dockerignore
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/workflows/docker-e2e.yml
delete mode 100644 .github/workflows/npm-ci.yml
create mode 100644 .husky/commit-msg
create mode 100755 .husky/pre-commit
create mode 100644 .hygen.js
create mode 100644 .hygen/seeds/create-document/module.ejs.t
create mode 100644 .hygen/seeds/create-document/run-seed-import.ejs.t
create mode 100644 .hygen/seeds/create-document/run-seed-service.ejs.t
create mode 100644 .hygen/seeds/create-document/seed-module-import.ejs.t
create mode 100644 .hygen/seeds/create-document/seed-module.ejs.t
create mode 100644 .hygen/seeds/create-document/service.ejs.t
create mode 100644 .hygen/seeds/create-relational/module.ejs.t
create mode 100644 .hygen/seeds/create-relational/run-seed-import.ejs.t
create mode 100644 .hygen/seeds/create-relational/run-seed-service.ejs.t
create mode 100644 .hygen/seeds/create-relational/seed-module-import.ejs.t
create mode 100644 .hygen/seeds/create-relational/seed-module.ejs.t
create mode 100644 .hygen/seeds/create-relational/service.ejs.t
create mode 100644 .install-scripts/helpers/replace.ts
create mode 100644 .install-scripts/index.ts
create mode 100644 .install-scripts/scripts/remove-auth-apple.ts
create mode 100644 .install-scripts/scripts/remove-auth-facebook.ts
create mode 100644 .install-scripts/scripts/remove-auth-google.ts
create mode 100644 .install-scripts/scripts/remove-auth-twitter.ts
create mode 100644 .install-scripts/scripts/remove-install-scripts.ts
create mode 100644 .install-scripts/scripts/remove-mongodb.ts
create mode 100644 .install-scripts/scripts/remove-postgresql.ts
create mode 100644 .nvmrc
create mode 100644 .vscode/extensions.json
create mode 100644 .vscode/settings.json
create mode 100644 CHANGELOG.md
create mode 100644 CODE_OF_CONDUCT.md
create mode 100644 Dockerfile
create mode 100644 Procfile
create mode 100644 commitlint.config.js
create mode 100644 docker-compose.document.ci.yaml
create mode 100644 docker-compose.document.test.yaml
create mode 100644 docker-compose.document.yaml
create mode 100644 docker-compose.relational.ci.yaml
create mode 100644 docker-compose.relational.test.yaml
create mode 100644 docker-compose.yaml
create mode 100644 docs/architecture.md
create mode 100644 docs/auth.md
create mode 100644 docs/automatic-update-dependencies.md
create mode 100644 docs/benchmarking.md
create mode 100644 docs/database.md
create mode 100644 docs/file-uploading.md
create mode 100644 docs/installing-and-running.md
create mode 100644 docs/introduction.md
create mode 100644 docs/readme.md
create mode 100644 docs/serialization.md
create mode 100644 docs/tests.md
create mode 100644 document.Dockerfile
create mode 100644 document.e2e.Dockerfile
create mode 100644 document.test.Dockerfile
create mode 100644 env-example-document
create mode 100644 env-example-relational
create mode 100644 maildev.Dockerfile
delete mode 100644 package-lock.json
create mode 100644 relational.e2e.Dockerfile
create mode 100644 relational.test.Dockerfile
create mode 100644 renovate.json
delete mode 100644 src/app.controller.spec.ts
delete mode 100644 src/app.controller.ts
create mode 100644 src/auth-apple/auth-apple.controller.ts
create mode 100644 src/auth-apple/auth-apple.module.ts
create mode 100644 src/auth-apple/auth-apple.service.ts
create mode 100644 src/auth-apple/config/apple-config.type.ts
create mode 100644 src/auth-apple/config/apple.config.ts
create mode 100644 src/auth-apple/dto/auth-apple-login.dto.ts
create mode 100644 src/auth-facebook/auth-facebook.controller.ts
create mode 100644 src/auth-facebook/auth-facebook.module.ts
create mode 100644 src/auth-facebook/auth-facebook.service.ts
create mode 100644 src/auth-facebook/config/facebook-config.type.ts
create mode 100644 src/auth-facebook/config/facebook.config.ts
create mode 100644 src/auth-facebook/dto/auth-facebook-login.dto.ts
create mode 100644 src/auth-facebook/interfaces/facebook.interface.ts
create mode 100644 src/auth-google/auth-google.controller.ts
create mode 100644 src/auth-google/auth-google.module.ts
create mode 100644 src/auth-google/auth-google.service.ts
create mode 100644 src/auth-google/config/google-config.type.ts
create mode 100644 src/auth-google/config/google.config.ts
create mode 100644 src/auth-google/dto/auth-google-login.dto.ts
create mode 100644 src/auth-twitter/auth-twitter.controller.ts
create mode 100644 src/auth-twitter/auth-twitter.module.ts
create mode 100644 src/auth-twitter/auth-twitter.service.ts
create mode 100644 src/auth-twitter/config/twitter-config.type.ts
create mode 100644 src/auth-twitter/config/twitter.config.ts
create mode 100644 src/auth-twitter/dto/auth-twitter-login.dto.ts
create mode 100644 src/auth/auth-providers.enum.ts
create mode 100644 src/auth/auth.controller.ts
create mode 100644 src/auth/auth.module.ts
create mode 100644 src/auth/auth.service.ts
create mode 100644 src/auth/config/auth-config.type.ts
create mode 100644 src/auth/config/auth.config.ts
create mode 100644 src/auth/dto/auth-confirm-email.dto.ts
create mode 100644 src/auth/dto/auth-email-login.dto.ts
create mode 100644 src/auth/dto/auth-forgot-password.dto.ts
create mode 100644 src/auth/dto/auth-register-login.dto.ts
create mode 100644 src/auth/dto/auth-reset-password.dto.ts
create mode 100644 src/auth/dto/auth-update.dto.ts
create mode 100644 src/auth/dto/login-response.dto.ts
create mode 100644 src/auth/dto/refresh-response.dto.ts
create mode 100644 src/auth/strategies/anonymous.strategy.ts
create mode 100644 src/auth/strategies/jwt-refresh.strategy.ts
create mode 100644 src/auth/strategies/jwt.strategy.ts
create mode 100644 src/auth/strategies/types/jwt-payload.type.ts
create mode 100644 src/auth/strategies/types/jwt-refresh-payload.type.ts
create mode 100644 src/config/app-config.type.ts
create mode 100644 src/config/app.config.ts
create mode 100644 src/config/config.type.ts
create mode 100644 src/database/config/database-config.type.ts
create mode 100644 src/database/config/database.config.ts
create mode 100644 src/database/data-source.ts
create mode 100644 src/database/migrations/1715028537217-CreateUser.ts
create mode 100644 src/database/mongoose-config.service.ts
create mode 100644 src/database/seeds/document/run-seed.ts
create mode 100644 src/database/seeds/document/seed.module.ts
create mode 100644 src/database/seeds/document/user/user-seed.module.ts
create mode 100644 src/database/seeds/document/user/user-seed.service.ts
create mode 100644 src/database/seeds/relational/role/role-seed.module.ts
create mode 100644 src/database/seeds/relational/role/role-seed.service.ts
create mode 100644 src/database/seeds/relational/run-seed.ts
create mode 100644 src/database/seeds/relational/seed.module.ts
create mode 100644 src/database/seeds/relational/status/status-seed.module.ts
create mode 100644 src/database/seeds/relational/status/status-seed.service.ts
create mode 100644 src/database/seeds/relational/user/user-seed.module.ts
create mode 100644 src/database/seeds/relational/user/user-seed.service.ts
create mode 100644 src/database/typeorm-config.service.ts
create mode 100644 src/files/config/file-config.type.ts
create mode 100644 src/files/config/file.config.ts
create mode 100644 src/files/domain/file.ts
create mode 100644 src/files/dto/file.dto.ts
create mode 100644 src/files/files.module.ts
create mode 100644 src/files/files.service.ts
create mode 100644 src/files/infrastructure/persistence/document/document-persistence.module.ts
create mode 100644 src/files/infrastructure/persistence/document/entities/file.schema.ts
create mode 100644 src/files/infrastructure/persistence/document/mappers/file.mapper.ts
create mode 100644 src/files/infrastructure/persistence/document/repositories/file.repository.ts
create mode 100644 src/files/infrastructure/persistence/file.repository.ts
create mode 100644 src/files/infrastructure/persistence/relational/entities/file.entity.ts
create mode 100644 src/files/infrastructure/persistence/relational/mappers/file.mapper.ts
create mode 100644 src/files/infrastructure/persistence/relational/relational-persistence.module.ts
create mode 100644 src/files/infrastructure/persistence/relational/repositories/file.repository.ts
create mode 100644 src/files/infrastructure/uploader/local/dto/file-response.dto.ts
create mode 100644 src/files/infrastructure/uploader/local/files.controller.ts
create mode 100644 src/files/infrastructure/uploader/local/files.module.ts
create mode 100644 src/files/infrastructure/uploader/local/files.service.ts
create mode 100644 src/files/infrastructure/uploader/s3-presigned/dto/file-response.dto.ts
create mode 100644 src/files/infrastructure/uploader/s3-presigned/dto/file.dto.ts
create mode 100644 src/files/infrastructure/uploader/s3-presigned/files.controller.ts
create mode 100644 src/files/infrastructure/uploader/s3-presigned/files.module.ts
create mode 100644 src/files/infrastructure/uploader/s3-presigned/files.service.ts
create mode 100644 src/files/infrastructure/uploader/s3/dto/file-response.dto.ts
create mode 100644 src/files/infrastructure/uploader/s3/files.controller.ts
create mode 100644 src/files/infrastructure/uploader/s3/files.module.ts
create mode 100644 src/files/infrastructure/uploader/s3/files.service.ts
create mode 100644 src/home/home.controller.ts
create mode 100644 src/home/home.module.ts
create mode 100644 src/home/home.service.ts
create mode 100644 src/i18n/en/common.json
create mode 100644 src/i18n/en/confirm-email.json
create mode 100644 src/i18n/en/confirm-new-email.json
create mode 100644 src/i18n/en/reset-password.json
create mode 100644 src/mail/config/mail-config.type.ts
create mode 100644 src/mail/config/mail.config.ts
create mode 100644 src/mail/interfaces/mail-data.interface.ts
create mode 100644 src/mail/mail-templates/activation.hbs
create mode 100644 src/mail/mail-templates/confirm-new-email.hbs
create mode 100644 src/mail/mail-templates/reset-password.hbs
create mode 100644 src/mail/mail.module.ts
create mode 100644 src/mail/mail.service.ts
create mode 100644 src/mailer/mailer.module.ts
create mode 100644 src/mailer/mailer.service.ts
create mode 100644 src/roles/domain/role.ts
create mode 100644 src/roles/dto/role.dto.ts
create mode 100644 src/roles/infrastructure/persistence/document/entities/role.schema.ts
create mode 100644 src/roles/infrastructure/persistence/relational/entities/role.entity.ts
create mode 100644 src/roles/roles.decorator.ts
create mode 100644 src/roles/roles.enum.ts
create mode 100644 src/roles/roles.guard.ts
create mode 100644 src/session/domain/session.ts
create mode 100644 src/session/infrastructure/persistence/document/document-persistence.module.ts
create mode 100644 src/session/infrastructure/persistence/document/entities/session.schema.ts
create mode 100644 src/session/infrastructure/persistence/document/mappers/session.mapper.ts
create mode 100644 src/session/infrastructure/persistence/document/repositories/session.repository.ts
create mode 100644 src/session/infrastructure/persistence/relational/entities/session.entity.ts
create mode 100644 src/session/infrastructure/persistence/relational/mappers/session.mapper.ts
create mode 100644 src/session/infrastructure/persistence/relational/relational-persistence.module.ts
create mode 100644 src/session/infrastructure/persistence/relational/repositories/session.repository.ts
create mode 100644 src/session/infrastructure/persistence/session.repository.ts
create mode 100644 src/session/session.module.ts
create mode 100644 src/session/session.service.ts
create mode 100644 src/social/interfaces/social.interface.ts
create mode 100644 src/social/tokens.ts
create mode 100644 src/statuses/domain/status.ts
create mode 100644 src/statuses/dto/status.dto.ts
create mode 100644 src/statuses/infrastructure/persistence/document/entities/status.schema.ts
create mode 100644 src/statuses/infrastructure/persistence/relational/entities/status.entity.ts
create mode 100644 src/statuses/statuses.enum.ts
create mode 100644 src/users/domain/user.ts
create mode 100644 src/users/dto/create-user.dto.ts
create mode 100644 src/users/dto/query-user.dto.ts
create mode 100644 src/users/dto/update-user.dto.ts
create mode 100644 src/users/infrastructure/persistence/document/document-persistence.module.ts
create mode 100644 src/users/infrastructure/persistence/document/entities/user.schema.ts
create mode 100644 src/users/infrastructure/persistence/document/mappers/user.mapper.ts
create mode 100644 src/users/infrastructure/persistence/document/repositories/user.repository.ts
create mode 100644 src/users/infrastructure/persistence/relational/entities/user.entity.ts
create mode 100644 src/users/infrastructure/persistence/relational/mappers/user.mapper.ts
create mode 100644 src/users/infrastructure/persistence/relational/relational-persistence.module.ts
create mode 100644 src/users/infrastructure/persistence/relational/repositories/user.repository.ts
create mode 100644 src/users/infrastructure/persistence/user.repository.ts
create mode 100644 src/users/users.controller.ts
create mode 100644 src/users/users.module.ts
create mode 100644 src/users/users.service.ts
create mode 100644 src/utils/deep-resolver.ts
create mode 100644 src/utils/document-entity-helper.ts
create mode 100644 src/utils/domain-to-document-condition.spec.ts
create mode 100644 src/utils/domain-to-document-condition.ts
create mode 100644 src/utils/dto/infinity-pagination-response.dto.ts
create mode 100644 src/utils/infinity-pagination.ts
create mode 100644 src/utils/relational-entity-helper.ts
create mode 100644 src/utils/serializer.interceptor.ts
create mode 100644 src/utils/transformers/lower-case.transformer.ts
create mode 100644 src/utils/types/deep-partial.type.ts
create mode 100644 src/utils/types/entity-condition.type.ts
create mode 100644 src/utils/types/maybe.type.ts
create mode 100644 src/utils/types/nullable.type.ts
create mode 100644 src/utils/types/or-never.type.ts
create mode 100644 src/utils/types/pagination-options.ts
create mode 100644 src/utils/validate-config.ts
create mode 100644 src/utils/validation-options.ts
create mode 100755 startup.document.ci.sh
create mode 100755 startup.document.dev.sh
create mode 100755 startup.document.test.sh
create mode 100755 startup.relational.ci.sh
create mode 100755 startup.relational.dev.sh
create mode 100755 startup.relational.test.sh
create mode 100644 test/admin/auth.e2e-spec.ts
create mode 100644 test/admin/users.e2e-spec.ts
delete mode 100644 test/app.e2e-spec.ts
create mode 100644 test/user/auth.e2e-spec.ts
create mode 100644 test/utils/constants.ts
create mode 100755 wait-for-it.sh
diff --git a/.all-contributorsrc b/.all-contributorsrc
new file mode 100644
index 0000000..425cec1
--- /dev/null
+++ b/.all-contributorsrc
@@ -0,0 +1,50 @@
+{
+ "projectName": "nestjs-template",
+ "projectOwner": "khulnasoft",
+ "files": [
+ "README.md"
+ ],
+ "commitType": "docs",
+ "commitConvention": "angular",
+ "contributorsPerLine": 7,
+ "contributors": [
+ {
+ "login": "Shchepotin",
+ "name": "Vladyslav Shchepotin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6001723?v=4",
+ "profile": "https://github.com/Shchepotin",
+ "contributions": [
+ "maintenance",
+ "doc",
+ "code"
+ ]
+ },
+ {
+ "login": "SergeiLomako",
+ "name": "SergeiLomako",
+ "avatar_url": "https://avatars.githubusercontent.com/u/31205374?v=4",
+ "profile": "https://github.com/SergeiLomako",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "ElenVlass",
+ "name": "Elena Vlasenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/72293912?v=4",
+ "profile": "https://github.com/ElenVlass",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "sars",
+ "name": "Rodion",
+ "avatar_url": "https://avatars.githubusercontent.com/u/226194?v=4",
+ "profile": "http://khulnasoft.com",
+ "contributions": [
+ "business"
+ ]
+ }
+ ]
+}
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..e893bc0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+/node_modules
+/.data
+/dist
+/files
diff --git a/.eslintrc.js b/.eslintrc.js
index 0fbd99b..b5eac02 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,24 +2,43 @@ module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
+ tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
- 'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
- 'prettier',
- 'prettier/@typescript-eslint',
+ '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',
+ 'no-unused-vars': 'off',
+ '@typescript-eslint/no-unused-vars': ['error'],
+ 'require-await': 'off',
+ '@typescript-eslint/require-await': 'error',
+ '@typescript-eslint/no-floating-promises': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ {
+ selector:
+ 'CallExpression[callee.object.name=configService][callee.property.name=/^(get|getOrThrow)$/]:not(:has([arguments.1] Property[key.name=infer][value.value=true])), CallExpression[callee.object.property.name=configService][callee.property.name=/^(get|getOrThrow)$/]:not(:has([arguments.1] Property[key.name=infer][value.value=true]))',
+ message:
+ 'Add "{ infer: true }" to configService.get() for correct typechecking. Example: configService.get("database.port", { infer: true })',
+ },
+ {
+ selector:
+ 'CallExpression[callee.name=it][arguments.0.value!=/^should/]',
+ message: '"it" should start with "should"',
+ },
+ ],
},
};
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..5f0e2a2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: Shchepotin
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Send '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. Windows]
+ - NodeJS Version [e.g. 18.16.0]
+ - Database [e.g. PostgreSQL]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/workflows/docker-e2e.yml b/.github/workflows/docker-e2e.yml
new file mode 100644
index 0000000..cab3c73
--- /dev/null
+++ b/.github/workflows/docker-e2e.yml
@@ -0,0 +1,41 @@
+name: NestJS API CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ #
+ - name: Run e2e tests for NestJS with TypeORM
+ id: relational
+ run: docker compose -f docker-compose.relational.ci.yaml --env-file env-example-relational -p ci-relational up --build --exit-code-from api
+
+ - name: Copy prod.log from container to host
+ if: ${{ failure() && steps.relational.conclusion == 'failure' }}
+ run: docker cp ci-relational-api-1:/usr/src/app/prod.log .
+ #
+
+ #
+ - name: Run e2e tests for NestJS with Mongoose
+ id: document
+ run: docker compose -f docker-compose.document.ci.yaml --env-file env-example-document -p ci-document up --build --exit-code-from api
+
+ - name: Copy prod.log from container to host
+ if: ${{ failure() && steps.document.conclusion == 'failure' }}
+ run: docker cp ci-document-api-1:/usr/src/app/prod.log .
+ #
+
+ - name: Upload prod.log to artifacts for debugging
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: prod-logs
+ path: prod.log
diff --git a/.github/workflows/npm-ci.yml b/.github/workflows/npm-ci.yml
deleted file mode 100644
index 7f026ab..0000000
--- a/.github/workflows/npm-ci.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: Pnpm Install and Commit
-
-on:
- push:
- branches:
- - main
-
-jobs:
- build:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v2
-
- - name: Set up Node.js
- uses: actions/setup-node@v2
- with:
- node-version: '14'
-
- - name: Install dependencies
- run: npm install --no-frozen-lockfile
-
- - name: Commit changes
- run: |
- git config --global user.email "github-actions@github.com"
- git config --global user.name "GitHub Actions"
- git add .
- git commit -m "Update lockfile"
- git push
diff --git a/.gitignore b/.gitignore
index 09ea094..43ad4b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
logs
*.log
npm-debug.log*
+pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
@@ -32,3 +33,8 @@ lerna-debug.log*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
+
+.data
+/files
+.env
+/ormconfig.json
\ No newline at end of file
diff --git a/.husky/commit-msg b/.husky/commit-msg
new file mode 100644
index 0000000..30d445e
--- /dev/null
+++ b/.husky/commit-msg
@@ -0,0 +1 @@
+npx commitlint --edit
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..a9e7375
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,2 @@
+npm run lint
+npm run test -- --passWithNoTests
diff --git a/.hygen.js b/.hygen.js
new file mode 100644
index 0000000..d33626a
--- /dev/null
+++ b/.hygen.js
@@ -0,0 +1,3 @@
+module.exports = {
+ templates: `${__dirname}/.hygen`,
+};
diff --git a/.hygen/seeds/create-document/module.ejs.t b/.hygen/seeds/create-document/module.ejs.t
new file mode 100644
index 0000000..2e02a92
--- /dev/null
+++ b/.hygen/seeds/create-document/module.ejs.t
@@ -0,0 +1,21 @@
+---
+to: src/database/seeds/document/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.module.ts
+---
+import { Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { <%= name %>Schema, <%= name %>SchemaClass } from '../../../../<%= h.inflection.transform(name, ['pluralize', 'underscore', 'dasherize']) %>/infrastructure/persistence/document/entities/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>.schema';
+import { <%= name %>SeedService } from './<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.service';
+
+@Module({
+ imports: [
+ MongooseModule.forFeature([
+ {
+ name: <%= name %>SchemaClass.name,
+ schema: <%= name %>Schema,
+ },
+ ]),
+ ],
+ providers: [<%= name %>SeedService],
+ exports: [<%= name %>SeedService],
+})
+export class <%= name %>SeedModule {}
diff --git a/.hygen/seeds/create-document/run-seed-import.ejs.t b/.hygen/seeds/create-document/run-seed-import.ejs.t
new file mode 100644
index 0000000..8f4f71c
--- /dev/null
+++ b/.hygen/seeds/create-document/run-seed-import.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/document/run-seed.ts
+after: \@nestjs\/core
+---
+import { <%= name %>SeedService } from './<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.service';
\ No newline at end of file
diff --git a/.hygen/seeds/create-document/run-seed-service.ejs.t b/.hygen/seeds/create-document/run-seed-service.ejs.t
new file mode 100644
index 0000000..1fc573a
--- /dev/null
+++ b/.hygen/seeds/create-document/run-seed-service.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/document/run-seed.ts
+before: close
+---
+ await app.get(<%= name %>SeedService).run();
diff --git a/.hygen/seeds/create-document/seed-module-import.ejs.t b/.hygen/seeds/create-document/seed-module-import.ejs.t
new file mode 100644
index 0000000..f18e942
--- /dev/null
+++ b/.hygen/seeds/create-document/seed-module-import.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/document/seed.module.ts
+before: \@Module
+---
+import { <%= name %>SeedModule } from './<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.module';
diff --git a/.hygen/seeds/create-document/seed-module.ejs.t b/.hygen/seeds/create-document/seed-module.ejs.t
new file mode 100644
index 0000000..e48a145
--- /dev/null
+++ b/.hygen/seeds/create-document/seed-module.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/document/seed.module.ts
+after: imports
+---
+ <%= name %>SeedModule,
\ No newline at end of file
diff --git a/.hygen/seeds/create-document/service.ejs.t b/.hygen/seeds/create-document/service.ejs.t
new file mode 100644
index 0000000..bdd080e
--- /dev/null
+++ b/.hygen/seeds/create-document/service.ejs.t
@@ -0,0 +1,24 @@
+---
+to: src/database/seeds/document/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.service.ts
+---
+import { Injectable } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model } from 'mongoose';
+import { <%= name %>SchemaClass } from '../../../../<%= h.inflection.transform(name, ['pluralize', 'underscore', 'dasherize']) %>/infrastructure/persistence/document/entities/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>.schema';
+
+@Injectable()
+export class <%= name %>SeedService {
+ constructor(
+ @InjectModel(<%= name %>SchemaClass.name)
+ private readonly model: Model<<%= name %>SchemaClass>,
+ ) {}
+
+ async run() {
+ const count = await this.model.countDocuments();
+
+ if (count === 0) {
+ const data = new this.model({});
+ await data.save();
+ }
+ }
+}
diff --git a/.hygen/seeds/create-relational/module.ejs.t b/.hygen/seeds/create-relational/module.ejs.t
new file mode 100644
index 0000000..2009892
--- /dev/null
+++ b/.hygen/seeds/create-relational/module.ejs.t
@@ -0,0 +1,14 @@
+---
+to: src/database/seeds/relational/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.module.ts
+---
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { <%= name %>Entity } from '../../../../<%= h.inflection.transform(name, ['pluralize', 'underscore', 'dasherize']) %>/infrastructure/persistence/relational/entities/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>.entity';
+import { <%= name %>SeedService } from './<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.service';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([<%= name %>Entity])],
+ providers: [<%= name %>SeedService],
+ exports: [<%= name %>SeedService],
+})
+export class <%= name %>SeedModule {}
diff --git a/.hygen/seeds/create-relational/run-seed-import.ejs.t b/.hygen/seeds/create-relational/run-seed-import.ejs.t
new file mode 100644
index 0000000..ef86fb3
--- /dev/null
+++ b/.hygen/seeds/create-relational/run-seed-import.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/relational/run-seed.ts
+after: \@nestjs\/core
+---
+import { <%= name %>SeedService } from './<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.service';
\ No newline at end of file
diff --git a/.hygen/seeds/create-relational/run-seed-service.ejs.t b/.hygen/seeds/create-relational/run-seed-service.ejs.t
new file mode 100644
index 0000000..7bc5d1d
--- /dev/null
+++ b/.hygen/seeds/create-relational/run-seed-service.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/relational/run-seed.ts
+before: close
+---
+ await app.get(<%= name %>SeedService).run();
diff --git a/.hygen/seeds/create-relational/seed-module-import.ejs.t b/.hygen/seeds/create-relational/seed-module-import.ejs.t
new file mode 100644
index 0000000..e9c4780
--- /dev/null
+++ b/.hygen/seeds/create-relational/seed-module-import.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/relational/seed.module.ts
+before: \@Module
+---
+import { <%= name %>SeedModule } from './<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.module';
diff --git a/.hygen/seeds/create-relational/seed-module.ejs.t b/.hygen/seeds/create-relational/seed-module.ejs.t
new file mode 100644
index 0000000..b40a9ad
--- /dev/null
+++ b/.hygen/seeds/create-relational/seed-module.ejs.t
@@ -0,0 +1,6 @@
+---
+inject: true
+to: src/database/seeds/relational/seed.module.ts
+after: imports
+---
+ <%= name %>SeedModule,
\ No newline at end of file
diff --git a/.hygen/seeds/create-relational/service.ejs.t b/.hygen/seeds/create-relational/service.ejs.t
new file mode 100644
index 0000000..9243c13
--- /dev/null
+++ b/.hygen/seeds/create-relational/service.ejs.t
@@ -0,0 +1,23 @@
+---
+to: src/database/seeds/relational/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>-seed.service.ts
+---
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { <%= name %>Entity } from '../../../../<%= h.inflection.transform(name, ['pluralize', 'underscore', 'dasherize']) %>/infrastructure/persistence/relational/entities/<%= h.inflection.transform(name, ['underscore', 'dasherize']) %>.entity';
+import { Repository } from 'typeorm';
+
+@Injectable()
+export class <%= name %>SeedService {
+ constructor(
+ @InjectRepository(<%= name %>Entity)
+ private repository: Repository<<%= name %>Entity>,
+ ) {}
+
+ async run() {
+ const count = await this.repository.count();
+
+ if (count === 0) {
+ await this.repository.save(this.repository.create({}));
+ }
+ }
+}
diff --git a/.install-scripts/helpers/replace.ts b/.install-scripts/helpers/replace.ts
new file mode 100644
index 0000000..455f0ba
--- /dev/null
+++ b/.install-scripts/helpers/replace.ts
@@ -0,0 +1,25 @@
+import fs from 'fs';
+
+const replace = (params: {
+ path: string;
+ actions: Array<{
+ find: string | RegExp;
+ replace: string;
+ }>;
+}) => {
+ const { path, actions } = params;
+
+ try {
+ let content = fs.readFileSync(path, 'utf-8');
+
+ actions.forEach((action) => {
+ content = content.replace(action.find, action.replace);
+ });
+
+ fs.writeFileSync(path, content, 'utf-8');
+ } catch (error) {
+ console.error(`Error replacing text in ${path}:`, error.message);
+ }
+};
+
+export default replace;
diff --git a/.install-scripts/index.ts b/.install-scripts/index.ts
new file mode 100644
index 0000000..857db4f
--- /dev/null
+++ b/.install-scripts/index.ts
@@ -0,0 +1,81 @@
+import prompts from 'prompts';
+import removeFacebookAuth from './scripts/remove-auth-facebook';
+import removeGoogleAuth from './scripts/remove-auth-google';
+import removeAppleAuth from './scripts/remove-auth-apple';
+import removeTwitterAuth from './scripts/remove-auth-twitter';
+import removeInstallScripts from './scripts/remove-install-scripts';
+import removePostgreSql from './scripts/remove-postgresql';
+import removeMongoDb from './scripts/remove-mongodb';
+
+(async () => {
+ const response = await prompts(
+ [
+ {
+ type: 'select',
+ name: 'database',
+ message: 'Which database do you want to use?',
+ choices: [
+ { title: 'PostgreSQL and MongoDB', value: 'pg-mongo' },
+ { title: 'PostgreSQL', value: 'pg' },
+ { title: 'MongoDB', value: 'mongo' },
+ ],
+ },
+ {
+ type: 'confirm',
+ name: 'isAuthFacebook',
+ message: 'Include Facebook auth?',
+ initial: true,
+ },
+ {
+ type: 'confirm',
+ name: 'isAuthGoogle',
+ message: 'Include Google auth?',
+ initial: true,
+ },
+ {
+ type: 'confirm',
+ name: 'isAuthTwitter',
+ message: 'Include Twitter auth?',
+ initial: true,
+ },
+ {
+ type: 'confirm',
+ name: 'isAuthApple',
+ message: 'Include Apple auth?',
+ initial: true,
+ },
+ ],
+ {
+ onCancel() {
+ process.exit(1);
+ },
+ },
+ );
+
+ if (response.database === 'mongo') {
+ removePostgreSql();
+ }
+
+ if (response.database === 'pg') {
+ removeMongoDb();
+ }
+
+ if (!response.isAuthFacebook) {
+ removeFacebookAuth();
+ }
+
+ if (!response.isAuthGoogle) {
+ removeGoogleAuth();
+ }
+
+ if (!response.isAuthTwitter) {
+ removeTwitterAuth();
+ }
+
+ if (!response.isAuthApple) {
+ removeAppleAuth();
+ }
+
+ removeInstallScripts();
+ process.exit(0);
+})();
diff --git a/.install-scripts/scripts/remove-auth-apple.ts b/.install-scripts/scripts/remove-auth-apple.ts
new file mode 100644
index 0000000..c6213ca
--- /dev/null
+++ b/.install-scripts/scripts/remove-auth-apple.ts
@@ -0,0 +1,55 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removeAppleAuth = async () => {
+ replace({
+ path: path.join(process.cwd(), 'src', 'app.module.ts'),
+ actions: [
+ {
+ find: /\s*AuthAppleModule\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*appleConfig\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ AuthAppleModule \} from \'\.\/auth\-apple\/auth\-apple\.module\'\;.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import appleConfig from \'\.\/auth\-apple\/config\/apple\.config\'\;.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'config', 'config.type.ts'),
+ actions: [
+ {
+ find: /\s*apple\: AppleConfig.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ AppleConfig \}.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /\s*\"apple-signin-auth\":.*/g,
+ replace: '',
+ },
+ ],
+ });
+ fs.rmSync(path.join(process.cwd(), 'src', 'auth-apple'), {
+ recursive: true,
+ force: true,
+ });
+};
+
+export default removeAppleAuth;
diff --git a/.install-scripts/scripts/remove-auth-facebook.ts b/.install-scripts/scripts/remove-auth-facebook.ts
new file mode 100644
index 0000000..709ab81
--- /dev/null
+++ b/.install-scripts/scripts/remove-auth-facebook.ts
@@ -0,0 +1,60 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removeFacebookAuth = async () => {
+ replace({
+ path: path.join(process.cwd(), 'src', 'app.module.ts'),
+ actions: [
+ {
+ find: /\s*AuthFacebookModule\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*facebookConfig\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ AuthFacebookModule \} from '\.\/auth\-facebook\/auth\-facebook\.module'\;.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import facebookConfig from '\.\/auth\-facebook\/config\/facebook\.config'\;.*/g,
+ replace: '',
+ },
+ ],
+ });
+
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /\s*\"fb\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"@types\/facebook\-js\-sdk\":.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'config', 'config.type.ts'),
+ actions: [
+ {
+ find: /\s*facebook\: FacebookConfig.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ FacebookConfig \}.*/g,
+ replace: '',
+ },
+ ],
+ });
+ fs.rmSync(path.join(process.cwd(), 'src', 'auth-facebook'), {
+ recursive: true,
+ force: true,
+ });
+};
+
+export default removeFacebookAuth;
diff --git a/.install-scripts/scripts/remove-auth-google.ts b/.install-scripts/scripts/remove-auth-google.ts
new file mode 100644
index 0000000..8b1e05e
--- /dev/null
+++ b/.install-scripts/scripts/remove-auth-google.ts
@@ -0,0 +1,55 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removeGoogleAuth = async () => {
+ replace({
+ path: path.join(process.cwd(), 'src', 'app.module.ts'),
+ actions: [
+ {
+ find: /\s*AuthGoogleModule\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*googleConfig\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ AuthGoogleModule \} from \'\.\/auth\-google\/auth\-google\.module\'\;.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import googleConfig from \'\.\/auth\-google\/config\/google\.config\'\;.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'config', 'config.type.ts'),
+ actions: [
+ {
+ find: /\s*google\: GoogleConfig.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ GoogleConfig \}.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /\s*\"google-auth-library\":.*/g,
+ replace: '',
+ },
+ ],
+ });
+ fs.rmSync(path.join(process.cwd(), 'src', 'auth-google'), {
+ recursive: true,
+ force: true,
+ });
+};
+
+export default removeGoogleAuth;
diff --git a/.install-scripts/scripts/remove-auth-twitter.ts b/.install-scripts/scripts/remove-auth-twitter.ts
new file mode 100644
index 0000000..a2e40cf
--- /dev/null
+++ b/.install-scripts/scripts/remove-auth-twitter.ts
@@ -0,0 +1,63 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removeTwitterAuth = async () => {
+ replace({
+ path: path.join(process.cwd(), 'src', 'app.module.ts'),
+ actions: [
+ {
+ find: /\s*AuthTwitterModule\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*twitterConfig\,.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ AuthTwitterModule \} from \'\.\/auth-twitter\/auth-twitter\.module\'\;.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import twitterConfig from \'\.\/auth-twitter\/config\/twitter\.config\'\;.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'config', 'config.type.ts'),
+ actions: [
+ {
+ find: /\s*twitter\: TwitterConfig.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ TwitterConfig \}.*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /,\s*\"twitter\":.*\"/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"twitter\":.*\,/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"@types\/twitter\":.*/g,
+ replace: '',
+ },
+ ],
+ });
+ fs.rmSync(path.join(process.cwd(), 'src', 'auth-twitter'), {
+ recursive: true,
+ force: true,
+ });
+};
+
+export default removeTwitterAuth;
diff --git a/.install-scripts/scripts/remove-install-scripts.ts b/.install-scripts/scripts/remove-install-scripts.ts
new file mode 100644
index 0000000..7e704ba
--- /dev/null
+++ b/.install-scripts/scripts/remove-install-scripts.ts
@@ -0,0 +1,25 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removeInstallScripts = async () => {
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /\s*\"app:config\".*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"@types\/prompts\"\:.*/g,
+ replace: '',
+ },
+ ],
+ });
+ fs.rmSync(path.join(process.cwd(), '.install-scripts'), {
+ recursive: true,
+ force: true,
+ });
+};
+
+export default removeInstallScripts;
diff --git a/.install-scripts/scripts/remove-mongodb.ts b/.install-scripts/scripts/remove-mongodb.ts
new file mode 100644
index 0000000..f026f02
--- /dev/null
+++ b/.install-scripts/scripts/remove-mongodb.ts
@@ -0,0 +1,340 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removeMongoDb = async () => {
+ const filesToRemove = [
+ path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'persistence',
+ 'document',
+ ),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'session',
+ 'infrastructure',
+ 'persistence',
+ 'document',
+ ),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'users',
+ 'infrastructure',
+ 'persistence',
+ 'document',
+ ),
+ path.join(process.cwd(), 'src', 'database', 'mongoose-config.service.ts'),
+ path.join(process.cwd(), 'src', 'database', 'seeds', 'document'),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'roles',
+ 'infrastructure',
+ 'persistence',
+ 'document',
+ ),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'statuses',
+ 'infrastructure',
+ 'persistence',
+ 'document',
+ ),
+ path.join(process.cwd(), 'env-example-document'),
+ path.join(process.cwd(), 'docker-compose.document.ci.yaml'),
+ path.join(process.cwd(), 'docker-compose.document.test.yaml'),
+ path.join(process.cwd(), 'docker-compose.document.yaml'),
+ path.join(process.cwd(), 'startup.document.ci.sh'),
+ path.join(process.cwd(), 'startup.document.dev.sh'),
+ path.join(process.cwd(), 'startup.document.test.sh'),
+ path.join(process.cwd(), 'document.Dockerfile'),
+ path.join(process.cwd(), 'document.e2e.Dockerfile'),
+ path.join(process.cwd(), 'document.test.Dockerfile'),
+ path.join(process.cwd(), '.hygen', 'seeds', 'create-document'),
+ path.join(process.cwd(), 'src', 'utils', 'document-entity-helper.ts'),
+ path.join(process.cwd(), 'src', 'utils', 'domain-to-document-condition.ts'),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'utils',
+ 'domain-to-document-condition.spec.ts',
+ ),
+ ];
+
+ replace({
+ path: path.join(process.cwd(), '.github', 'workflows', 'docker-e2e.yml'),
+ actions: [
+ {
+ find: /\# .*\# <\/database-document-block>/gs,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'app.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructureDatabaseModule = TypeOrmModule.forRootAsync({
+ useClass: TypeOrmConfigService,
+ dataSourceFactory: async (options: DataSourceOptions) => {
+ return new DataSource(options).initialize();
+ },
+});`,
+ },
+ {
+ find: /\s*import \{ MongooseModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ MongooseConfigService \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'files', 'files.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = RelationalFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ DocumentFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'uploader',
+ 'local',
+ 'files.module.ts',
+ ),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = RelationalFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ DocumentFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'uploader',
+ 's3',
+ 'files.module.ts',
+ ),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = RelationalFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ DocumentFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'uploader',
+ 's3-presigned',
+ 'files.module.ts',
+ ),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = RelationalFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ DocumentFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'session', 'session.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = RelationalSessionPersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ DocumentSessionPersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'users', 'users.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = RelationalUserPersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ DocumentUserPersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'users', 'domain', 'user.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const idType = Number;`,
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'statuses', 'domain', 'status.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const idType = Number;`,
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'roles', 'domain', 'role.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const idType = Number;`,
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /\s*\"@nestjs\/mongoose\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"mongoose\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"seed:run:document\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"seed:create:document\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"test:e2e:document:docker\":.*/g,
+ replace: '',
+ },
+ ],
+ });
+
+ filesToRemove.map((file) => {
+ fs.rmSync(file, {
+ recursive: true,
+ force: true,
+ });
+ });
+};
+
+export default removeMongoDb;
diff --git a/.install-scripts/scripts/remove-postgresql.ts b/.install-scripts/scripts/remove-postgresql.ts
new file mode 100644
index 0000000..f75316f
--- /dev/null
+++ b/.install-scripts/scripts/remove-postgresql.ts
@@ -0,0 +1,364 @@
+import replace from '../helpers/replace';
+import path from 'path';
+import fs from 'fs';
+
+const removePostgreSql = async () => {
+ const filesToRemove = [
+ path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'persistence',
+ 'relational',
+ ),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'session',
+ 'infrastructure',
+ 'persistence',
+ 'relational',
+ ),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'users',
+ 'infrastructure',
+ 'persistence',
+ 'relational',
+ ),
+ path.join(process.cwd(), 'src', 'database', 'migrations'),
+ path.join(process.cwd(), 'src', 'database', 'data-source.ts'),
+ path.join(process.cwd(), 'src', 'database', 'typeorm-config.service.ts'),
+ path.join(process.cwd(), 'src', 'database', 'seeds', 'relational'),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'roles',
+ 'infrastructure',
+ 'persistence',
+ 'relational',
+ ),
+ path.join(
+ process.cwd(),
+ 'src',
+ 'statuses',
+ 'infrastructure',
+ 'persistence',
+ 'relational',
+ ),
+ path.join(process.cwd(), 'env-example-relational'),
+ path.join(process.cwd(), 'docker-compose.relational.ci.yaml'),
+ path.join(process.cwd(), 'docker-compose.relational.test.yaml'),
+ path.join(process.cwd(), 'docker-compose.yaml'),
+ path.join(process.cwd(), 'startup.relational.ci.sh'),
+ path.join(process.cwd(), 'startup.relational.test.sh'),
+ path.join(process.cwd(), 'startup.relational.dev.sh'),
+ path.join(process.cwd(), 'Dockerfile'),
+ path.join(process.cwd(), 'relational.e2e.Dockerfile'),
+ path.join(process.cwd(), 'relational.test.Dockerfile'),
+ path.join(process.cwd(), '.hygen', 'seeds', 'create-relational'),
+ path.join(process.cwd(), 'src', 'utils', 'relational-entity-helper.ts'),
+ ];
+
+ replace({
+ path: path.join(process.cwd(), '.github', 'workflows', 'docker-e2e.yml'),
+ actions: [
+ {
+ find: /\# .*\# <\/database-relational-block>/gs,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'app.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructureDatabaseModule = MongooseModule.forRootAsync({
+ useClass: MongooseConfigService,
+});`,
+ },
+ {
+ find: /\s*import \{ TypeOrmModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ TypeOrmConfigService \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DataSource, DataSourceOptions \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'files', 'files.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = DocumentFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ RelationalFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'uploader',
+ 'local',
+ 'files.module.ts',
+ ),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = DocumentFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ RelationalFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'uploader',
+ 's3',
+ 'files.module.ts',
+ ),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = DocumentFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ RelationalFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(
+ process.cwd(),
+ 'src',
+ 'files',
+ 'infrastructure',
+ 'uploader',
+ 's3-presigned',
+ 'files.module.ts',
+ ),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = DocumentFilePersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ RelationalFilePersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'session', 'session.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = DocumentSessionPersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ RelationalSessionPersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'users', 'users.module.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const infrastructurePersistenceModule = DocumentUserPersistenceModule;`,
+ },
+ {
+ find: /\s*import \{ RelationalUserPersistenceModule \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'users', 'domain', 'user.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const idType = String;`,
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'statuses', 'domain', 'status.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const idType = String;`,
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'src', 'roles', 'domain', 'role.ts'),
+ actions: [
+ {
+ find: /\/\/ .*\/\/ <\/database-block>/gs,
+ replace: `const idType = String;`,
+ },
+ {
+ find: /\s*import \{ DatabaseConfig \} from .*/g,
+ replace: '',
+ },
+ {
+ find: /\s*import databaseConfig from .*/g,
+ replace: '',
+ },
+ ],
+ });
+ replace({
+ path: path.join(process.cwd(), 'package.json'),
+ actions: [
+ {
+ find: /\s*\"@nestjs\/typeorm\":.*/g,
+ replace: '',
+ },
+ {
+ find: /,\s*\"typeorm\":.*\"/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"typeorm\":.*\,/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"migration:generate\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"migration:create\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"migration:run\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"migration:revert\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"seed:create:relational\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"seed:run:relational\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"schema:drop\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"test:e2e:relational:docker\":.*/g,
+ replace: '',
+ },
+ {
+ find: /\s*\"pg\":.*/g,
+ replace: '',
+ },
+ ],
+ });
+
+ filesToRemove.map((file) => {
+ fs.rmSync(file, {
+ recursive: true,
+ force: true,
+ });
+ });
+};
+
+export default removePostgreSql;
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..8783404
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+20.12.2
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..025cf0b
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,6 @@
+{
+ "recommendations": [
+ "yzhang.markdown-all-in-one",
+ "DavidAnson.vscode-markdownlint"
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..a1d2b94
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "markdown.extension.toc.levels": "2..6",
+ "markdown.extension.orderedList.autoRenumber": false,
+ "typescript.preferences.importModuleSpecifier": "relative"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..68f05f0
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+. Translations are available at
+.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..3f5f941
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,22 @@
+FROM node:20.12.2-alpine
+
+RUN apk add --no-cache bash
+RUN npm i -g @nestjs/cli typescript ts-node
+
+COPY package*.json /tmp/app/
+RUN cd /tmp/app && npm install
+
+COPY . /usr/src/app
+RUN cp -a /tmp/app/node_modules /usr/src/app
+COPY ./wait-for-it.sh /opt/wait-for-it.sh
+RUN chmod +x /opt/wait-for-it.sh
+COPY ./startup.relational.dev.sh /opt/startup.relational.dev.sh
+RUN chmod +x /opt/startup.relational.dev.sh
+RUN sed -i 's/\r//g' /opt/wait-for-it.sh
+RUN sed -i 's/\r//g' /opt/startup.relational.dev.sh
+
+WORKDIR /usr/src/app
+RUN if [ ! -f .env ]; then cp env-example-relational .env; fi
+RUN npm run build
+
+CMD ["/opt/startup.relational.dev.sh"]
diff --git a/LICENSE b/LICENSE
index 073596d..7e30d4f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 KhulnaSoft Ltd.
+Copyright (c) 2024 KhulnaSoft, Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000..c5bea1f
--- /dev/null
+++ b/Procfile
@@ -0,0 +1,2 @@
+web: npm run start:prod
+release: echo '' > .env && npm run migration:run && npm run seed:run:relational
\ No newline at end of file
diff --git a/README.md b/README.md
index 096fb11..9e50cb5 100644
--- a/README.md
+++ b/README.md
@@ -20,46 +20,60 @@
-## Description
-[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
+## Description
-## Installation
+NestJS REST API template for a typical project
-```bash
-$ npm install
-```
+[Full documentation here](/docs/readme.md)
-## Running the app
+Demo:
-```bash
-# development
-$ npm run start
+Belongs to the [bc templates](https://bctemplates.com/) ecosystem
-# watch mode
-$ npm run start:dev
+## Table of Contents
-# production mode
-$ npm run start:prod
-```
+- [Features](#features)
+- [Contributors](#contributors)
+- [Support](#support)
-## Test
+## Features
-```bash
-# unit tests
-$ npm run test
+- [x] Database. Support [TypeORM](https://www.npmjs.com/package/typeorm) and [Mongoose](https://www.npmjs.com/package/mongoose).
+- [x] Seeding.
+- [x] Config Service ([@nestjs/config](https://www.npmjs.com/package/@nestjs/config)).
+- [x] Mailing ([nodemailer](https://www.npmjs.com/package/nodemailer)).
+- [x] Sign in and sign up via email.
+- [x] Social sign in (Apple, Facebook, Google, Twitter).
+- [x] Admin and User roles.
+- [x] Internationalization/Translations (I18N) ([nestjs-i18n](https://www.npmjs.com/package/nestjs-i18n)).
+- [x] File uploads. Support local and Amazon S3 drivers.
+- [x] Swagger.
+- [x] E2E and units tests.
+- [x] Docker.
+- [x] CI (Github Actions).
-# e2e tests
-$ npm run test:e2e
+## Contributors
-# test coverage
-$ npm run test:cov
-```
+
+
+
+
-## Support
+
+
-Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
+
-## License
+## Support
- Nest is [MIT licensed](LICENSE).
+If you seek consulting, support, or wish to collaborate, please contact us via [templates@khulnasoft.com](mailto:templates@khulnasoft.com). For any inquiries regarding templates, feel free to ask on [GitHub Discussions](https://github.com/khulnasoft/nestjs-template/discussions) or [Discord](https://discord.com/channels/520622812742811698/1197293125434093701).
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..84dcb12
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: ['@commitlint/config-conventional'],
+};
diff --git a/docker-compose.document.ci.yaml b/docker-compose.document.ci.yaml
new file mode 100644
index 0000000..75fd11d
--- /dev/null
+++ b/docker-compose.document.ci.yaml
@@ -0,0 +1,30 @@
+services:
+ mongo:
+ image: mongo:7.0.9
+ restart: always
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: ${DATABASE_USERNAME}
+ MONGO_INITDB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
+ expose:
+ - 27017
+
+ maildev:
+ build:
+ context: .
+ dockerfile: maildev.Dockerfile
+ expose:
+ - 1080
+ - 1025
+
+ # Uncomment to use redis
+ # redis:
+ # image: redis:7-alpine
+ # expose:
+ # - 6379
+
+ api:
+ build:
+ context: .
+ dockerfile: document.e2e.Dockerfile
+ env_file:
+ - env-example-document
diff --git a/docker-compose.document.test.yaml b/docker-compose.document.test.yaml
new file mode 100644
index 0000000..922b985
--- /dev/null
+++ b/docker-compose.document.test.yaml
@@ -0,0 +1,33 @@
+services:
+ mongo:
+ image: mongo:7.0.9
+ restart: always
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: ${DATABASE_USERNAME}
+ MONGO_INITDB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
+ expose:
+ - 27017
+
+ maildev:
+ build:
+ context: .
+ dockerfile: maildev.Dockerfile
+ expose:
+ - 1080
+ - 1025
+
+ # Uncomment to use redis
+ # redis:
+ # image: redis:7-alpine
+ # expose:
+ # - 6379
+
+ api:
+ build:
+ context: .
+ dockerfile: document.test.Dockerfile
+ env_file:
+ - env-example-document
+ volumes:
+ - ./src:/usr/src/app/src
+ - ./test:/usr/src/app/test
diff --git a/docker-compose.document.yaml b/docker-compose.document.yaml
new file mode 100644
index 0000000..46a8731
--- /dev/null
+++ b/docker-compose.document.yaml
@@ -0,0 +1,45 @@
+services:
+ maildev:
+ build:
+ context: .
+ dockerfile: maildev.Dockerfile
+ ports:
+ - ${MAIL_CLIENT_PORT}:1080
+ - ${MAIL_PORT}:1025
+
+ mongo:
+ image: mongo:7.0.9
+ restart: always
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: ${DATABASE_USERNAME}
+ MONGO_INITDB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
+ volumes:
+ - template-mongo-db:/data/db
+ ports:
+ - ${DATABASE_PORT}:27017
+
+ mongo-express:
+ image: mongo-express
+ restart: always
+ ports:
+ - 8081:8081
+ environment:
+ ME_CONFIG_BASICAUTH_USERNAME: ${DATABASE_USERNAME}
+ ME_CONFIG_BASICAUTH_PASSWORD: ${DATABASE_PASSWORD}
+ ME_CONFIG_MONGODB_URL: mongodb://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@mongo:${DATABASE_PORT}/
+
+ # Uncomment to use redis
+ # redis:
+ # image: redis:7-alpine
+ # ports:
+ # - 6379:6379
+
+ api:
+ build:
+ context: .
+ dockerfile: document.Dockerfile
+ ports:
+ - ${APP_PORT}:${APP_PORT}
+
+volumes:
+ template-mongo-db:
diff --git a/docker-compose.relational.ci.yaml b/docker-compose.relational.ci.yaml
new file mode 100644
index 0000000..60bb9f4
--- /dev/null
+++ b/docker-compose.relational.ci.yaml
@@ -0,0 +1,30 @@
+services:
+ postgres:
+ image: postgres:16.3-alpine
+ expose:
+ - 5432
+ environment:
+ POSTGRES_USER: ${DATABASE_USERNAME}
+ POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
+ POSTGRES_DB: ${DATABASE_NAME}
+
+ maildev:
+ build:
+ context: .
+ dockerfile: maildev.Dockerfile
+ expose:
+ - 1080
+ - 1025
+
+ # Uncomment to use redis
+ # redis:
+ # image: redis:7-alpine
+ # expose:
+ # - 6379
+
+ api:
+ build:
+ context: .
+ dockerfile: relational.e2e.Dockerfile
+ env_file:
+ - env-example-relational
diff --git a/docker-compose.relational.test.yaml b/docker-compose.relational.test.yaml
new file mode 100644
index 0000000..0c8b3ed
--- /dev/null
+++ b/docker-compose.relational.test.yaml
@@ -0,0 +1,33 @@
+services:
+ postgres:
+ image: postgres:16.3-alpine
+ expose:
+ - 5432
+ environment:
+ POSTGRES_USER: ${DATABASE_USERNAME}
+ POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
+ POSTGRES_DB: ${DATABASE_NAME}
+
+ maildev:
+ build:
+ context: .
+ dockerfile: maildev.Dockerfile
+ expose:
+ - 1080
+ - 1025
+
+ # Uncomment to use redis
+ # redis:
+ # image: redis:7-alpine
+ # expose:
+ # - 6379
+
+ api:
+ build:
+ context: .
+ dockerfile: relational.test.Dockerfile
+ env_file:
+ - env-example-relational
+ volumes:
+ - ./src:/usr/src/app/src
+ - ./test:/usr/src/app/test
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..9638338
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,41 @@
+services:
+ postgres:
+ image: postgres:16.3-alpine
+ ports:
+ - ${DATABASE_PORT}:5432
+ volumes:
+ - template-db:/var/lib/postgresql/data
+ environment:
+ POSTGRES_USER: ${DATABASE_USERNAME}
+ POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
+ POSTGRES_DB: ${DATABASE_NAME}
+
+ maildev:
+ build:
+ context: .
+ dockerfile: maildev.Dockerfile
+ ports:
+ - ${MAIL_CLIENT_PORT}:1080
+ - ${MAIL_PORT}:1025
+
+ adminer:
+ image: adminer
+ restart: always
+ ports:
+ - 8080:8080
+
+ # Uncomment to use redis
+ # redis:
+ # image: redis:7-alpine
+ # ports:
+ # - 6379:6379
+
+ api:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ ports:
+ - ${APP_PORT}:${APP_PORT}
+
+volumes:
+ template-db:
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..ee57fdb
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,43 @@
+# Architecture
+
+---
+
+## Table of Contents
+
+- [Hexagonal Architecture](#hexagonal-architecture)
+- [Motivation](#motivation)
+- [Pitfalls](#pitfalls)
+- [FAQ](#faq)
+ - [I don't want to use Hexagonal Architecture. How can I use a traditional (three-tier) architecture for NestJS?](#i-dont-want-to-use-hexagonal-architecture-how-can-i-use-a-traditional-three-tier-architecture-for-nestjs)
+
+---
+
+## Hexagonal Architecture
+
+NestJS Template is based on [Hexagonal Architecture](https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)). This architecture is also known as Ports and Adapters.
+
+![Hexagonal Architecture Diagram](https://github.com/khulnasoft/nestjs-template/assets/6001723/6a6a763e-d1c9-43cc-910a-617cda3a71db)
+
+## Motivation
+
+The main reason for using Hexagonal Architecture is to separate the business logic from the infrastructure. This separation allows us to easily change the database, the way of uploading files, or any other infrastructure without changing the business logic.
+
+## Pitfalls
+
+Hexagonal Architecture takes more effort to implement, but it gives more flexibility and scalability. [If the time for your project is critical, you can use Three-tier architecture.](#i-dont-want-to-use-hexagonal-architecture-how-can-i-use-a-traditional-three-tier-architecture-for-nestjs)
+
+---
+
+## FAQ
+
+### I don't want to use Hexagonal Architecture. How can I use a traditional (three-tier) architecture for NestJS?
+
+You still can use [Three-tier Architecture](https://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture) `[controllers] -> [services] -> [data access]` near [Hexagonal Architecture](#hexagonal-architecture).
+
+Database example: Just keep the existing approach of getting data from the database for auth, files, etc, as is (with Hexagonal Architecture), but for new modules use repositories from TypeORM or models from Mongoose directly in [services](https://docs.nestjs.com/providers#services). Entities and Schemas are ready for this.
+
+---
+
+Previous: [Installing and Running](installing-and-running.md)
+
+Next: [Working with database](database.md)
diff --git a/docs/auth.md b/docs/auth.md
new file mode 100644
index 0000000..070e1a3
--- /dev/null
+++ b/docs/auth.md
@@ -0,0 +1,183 @@
+# Auth
+
+## Table of Contents
+
+- [General info](#general-info)
+ - [Auth via email flow](#auth-via-email-flow)
+ - [Auth via external services or social networks flow](#auth-via-external-services-or-social-networks-flow)
+- [Configure Auth](#configure-auth)
+- [Auth via Apple](#auth-via-apple)
+- [Auth via Facebook](#auth-via-facebook)
+- [Auth via Google](#auth-via-google)
+- [Auth via Twitter](#auth-via-twitter)
+- [About JWT strategy](#about-jwt-strategy)
+- [Refresh token flow](#refresh-token-flow)
+ - [Video example](#video-example)
+- [Logout](#logout)
+
+---
+
+## General info
+
+### Auth via email flow
+
+By default template used sign in and sign up via email and password.
+
+```mermaid
+sequenceDiagram
+ participant A as Fronted App (Web, Mobile, Desktop)
+ participant B as Backend App
+
+ A->>B: 1. Sign up via email and password
+ A->>B: 2. Sign in via email and password
+ B->>A: 3. Get a JWT token
+ A->>B: 4. Make any requests using a JWT token
+```
+
+
+
+### Auth via external services or social networks flow
+
+Also you can sign up via another external services or social networks like Apple, Facebook, Google, and Twitter.
+
+```mermaid
+sequenceDiagram
+ participant B as External Auth Services (Apple, Google, etc)
+ participant A as Fronted App (Web, Mobile, Desktop)
+ participant C as Backend App
+
+ A->>B: 1. Sign in through an external service
+ B->>A: 2. Get Access Token
+ A->>C: 3. Send Access Token to auth endpoint
+ C->>A: 4. Get a JWT token
+ A->>C: 5. Make any requests using a JWT token
+```
+
+For auth with external services or social networks you need:
+
+1. Sign in through an external service and get access token(s).
+1. Call one of endpoints with access token received in frontend app on 1-st step and get JWT token from the backend app.
+
+ ```text
+ POST /api/v1/auth/facebook/login
+
+ POST /api/v1/auth/google/login
+
+ POST /api/v1/auth/twitter/login
+
+ POST /api/v1/auth/apple/login
+ ```
+
+1. Make any requests using a JWT token
+
+---
+
+## Configure Auth
+
+1. Generate secret keys for `access token` and `refresh token`:
+
+ ```bash
+ node -e "console.log('\nAUTH_JWT_SECRET=' + require('crypto').randomBytes(256).toString('base64') + '\n\nAUTH_REFRESH_SECRET=' + require('crypto').randomBytes(256).toString('base64') + '\n\nAUTH_FORGOT_SECRET=' + require('crypto').randomBytes(256).toString('base64') + '\n\nAUTH_CONFIRM_EMAIL_SECRET=' + require('crypto').randomBytes(256).toString('base64'));"
+ ```
+
+1. Go to `/.env` and replace `AUTH_JWT_SECRET` and `AUTH_REFRESH_SECRET` with output from step 1.
+
+ ```text
+ AUTH_JWT_SECRET=HERE_SECRET_KEY_FROM_STEP_1
+ AUTH_REFRESH_SECRET=HERE_SECRET_KEY_FROM_STEP_1
+ ```
+
+## Auth via Apple
+
+1. [Set up your service on Apple](https://www.npmjs.com/package/apple-signin-auth)
+1. Change `APPLE_APP_AUDIENCE` in `.env`
+
+ ```text
+ APPLE_APP_AUDIENCE=["com.company", "com.company.web"]
+ ```
+
+## Auth via Facebook
+
+1. Go to https://developers.facebook.com/apps/creation/ and create a new app
+
+
+
+2. Go to `Settings` -> `Basic` and get `App ID` and `App Secret` from your app
+
+3. Change `FACEBOOK_APP_ID` and `FACEBOOK_APP_SECRET` in `.env`
+
+ ```text
+ FACEBOOK_APP_ID=123
+ FACEBOOK_APP_SECRET=abc
+ ```
+
+## Auth via Google
+
+1. You need a `CLIENT_ID`, `CLIENT_SECRET`. You can find these pieces of information by going to the [Developer Console](https://console.cloud.google.com/), clicking your project (if doesn't have create it here https://console.cloud.google.com/projectcreate) -> `APIs & services` -> `credentials`.
+1. Change `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in `.env`
+
+ ```text
+ GOOGLE_CLIENT_ID=abc
+ GOOGLE_CLIENT_SECRET=abc
+ ```
+
+## Auth via Twitter
+
+1. Set up your service on Twitter
+1. Change `TWITTER_CONSUMER_KEY` and `TWITTER_CONSUMER_SECRET` in `.env`
+
+ ```text
+ TWITTER_CONSUMER_KEY=abc
+ TWITTER_CONSUMER_SECRET=abc
+ ```
+
+## About JWT strategy
+
+In the `validate` method of the `src/auth/strategies/jwt.strategy.ts` file, you can see that we do not check if the user exists in the database because it is redundant, it may lose the benefits of the JWT approach and can affect the application performance.
+
+To better understand how JWT works, watch the video explanation https://www.youtube.com/watch?v=Y2H3DXDeS3Q and read this article https://jwt.io/introduction/
+
+```typescript
+// src/auth/strategies/jwt.strategy.ts
+
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
+ // ...
+
+ public validate(payload) {
+ if (!payload.id) {
+ throw new UnauthorizedException();
+ }
+
+ return payload;
+ }
+}
+```
+
+> If you need to get full user information, get it in services.
+
+## Refresh token flow
+
+1. On sign in (`POST /api/v1/auth/email/login`) you will receive `token`, `tokenExpires` and `refreshToken` in response.
+1. On each regular request you need to send `token` in `Authorization` header.
+1. If `token` is expired (check with `tokenExpires` property on client app) you need to send `refreshToken` to `POST /api/v1/auth/refresh` in `Authorization` header to refresh `token`. You will receive new `token`, `tokenExpires` and `refreshToken` in response.
+
+### Video example
+
+https://github.com/khulnasoft/nestjs-template/assets/6001723/f6fdcc89-5ec6-472b-a6fc-d24178ad1bbb
+
+## Logout
+
+1. Call following endpoint:
+
+ ```text
+ POST /api/v1/auth/logout
+ ```
+
+2. Remove `access token` and `refresh token` from your client app (cookies, localStorage, etc).
+
+---
+
+Previous: [Working with database](database.md)
+
+Next: [Serialization](serialization.md)
diff --git a/docs/automatic-update-dependencies.md b/docs/automatic-update-dependencies.md
new file mode 100644
index 0000000..cd36b23
--- /dev/null
+++ b/docs/automatic-update-dependencies.md
@@ -0,0 +1,7 @@
+# Automatic update of dependencies
+
+If you want to automatically update dependencies, you can connect [Renovate](https://github.com/marketplace/renovate) for your project.
+
+---
+
+Previous: [Benchmarking](benchmarking.md)
\ No newline at end of file
diff --git a/docs/benchmarking.md b/docs/benchmarking.md
new file mode 100644
index 0000000..72ee1b9
--- /dev/null
+++ b/docs/benchmarking.md
@@ -0,0 +1,17 @@
+# Test benchmarking
+
+## Table of Contents
+
+- [Apache Benchmark](#apache-benchmark)
+
+## Apache Benchmark
+
+```bash
+docker run --rm jordi/ab -n 100 -c 100 -T application/json -H "Authorization: Bearer USER_TOKEN" -v 2 http://:3000/api/v1/users
+```
+
+---
+
+Previous: [Tests](tests.md)
+
+Next: [Automatic update of dependencies](automatic-update-dependencies.md)
diff --git a/docs/database.md b/docs/database.md
new file mode 100644
index 0000000..818de3a
--- /dev/null
+++ b/docs/database.md
@@ -0,0 +1,298 @@
+# Work with database
+
+---
+
+## Table of Contents
+
+- [About databases](#about-databases)
+- [Working with database schema (TypeORM)](#working-with-database-schema-typeorm)
+ - [Generate migration](#generate-migration)
+ - [Run migration](#run-migration)
+ - [Revert migration](#revert-migration)
+ - [Drop all tables in database](#drop-all-tables-in-database)
+- [Working with database schema (Mongoose)](#working-with-database-schema-mongoose)
+ - [Create schema](#create-schema)
+- [Seeding (TypeORM)](#seeding-typeorm)
+ - [Creating seeds (TypeORM)](#creating-seeds-typeorm)
+ - [Run seed (TypeORM)](#run-seed-typeorm)
+ - [Factory and Faker (TypeORM)](#factory-and-faker-typeorm)
+- [Seeding (Mongoose)](#seeding-mongoose)
+ - [Creating seeds (Mongoose)](#creating-seeds-mongoose)
+ - [Run seed (Mongoose)](#run-seed-mongoose)
+- [Performance optimization (PostgreSQL + TypeORM)](#performance-optimization-postgresql--typeorm)
+ - [Indexes and Foreign Keys](#indexes-and-foreign-keys)
+ - [Max connections](#max-connections)
+- [Performance optimization (MongoDB + Mongoose)](#performance-optimization-mongodb--mongoose)
+ - [Design schema](#design-schema)
+
+---
+
+## About databases
+
+Template supports two types of databases: PostgreSQL with TypeORM and MongoDB with Mongoose. You can choose one of them or use both in your project. The choice of database depends on the requirements of your project.
+
+For support of both databases used Hexagonal Architecture. Hexagonal architecture takes more effort to implement, but it gives more flexibility and scalability. If the time for your project is critical, you can use Three-tier architecture: use repositories from TypeORM or models from Mongoose directly in [services](https://docs.nestjs.com/providers#services). Entities and Schemas are ready for this.
+
+## Working with database schema (TypeORM)
+
+### Generate migration
+
+1. Create entity file with extension `.entity.ts`. For example `post.entity.ts`:
+
+ ```ts
+ // /src/posts/infrastructure/persistence/relational/entities/post.entity.ts
+
+ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
+ import { EntityRelationalHelper } from '../../../../../utils/relational-entity-helper';
+
+ @Entity()
+ export class Post extends EntityRelationalHelper {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @Column()
+ title: string;
+
+ @Column()
+ body: string;
+
+ // Here any fields that you need
+ }
+ ```
+
+1. Next, generate migration file:
+
+ ```bash
+ npm run migration:generate -- src/database/migrations/CreatePostTable
+ ```
+
+1. Apply this migration to database via [npm run migration:run](#run-migration).
+
+### Run migration
+
+```bash
+npm run migration:run
+```
+
+### Revert migration
+
+```bash
+npm run migration:revert
+```
+
+### Drop all tables in database
+
+```bash
+npm run schema:drop
+```
+
+---
+
+## Working with database schema (Mongoose)
+
+### Create schema
+
+1. Create entity file with extension `.schema.ts`. For example `post.schema.ts`:
+
+ ```ts
+ // /src/posts/infrastructure/persistence/document/entities/post.schema.ts
+
+ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+ import { HydratedDocument } from 'mongoose';
+
+ export type PostSchemaDocument = HydratedDocument;
+
+ @Schema({
+ timestamps: true,
+ toJSON: {
+ virtuals: true,
+ getters: true,
+ },
+ })
+ export class PostSchemaClass extends EntityDocumentHelper {
+ @Prop()
+ title: string;
+
+ @Prop()
+ body: string;
+
+ // Here any fields that you need
+ }
+
+ export const PostSchema = SchemaFactory.createForClass(PostSchemaClass);
+ ```
+
+---
+
+## Seeding (TypeORM)
+
+### Creating seeds (TypeORM)
+
+1. Create seed file with `npm run seed:create:relational -- --name=Post`. Where `Post` is name of entity.
+1. Go to `src/database/seeds/relational/post/post-seed.service.ts`.
+1. In `run` method extend your logic.
+1. Run [npm run seed:run:relational](#run-seed-typeorm)
+
+### Run seed (TypeORM)
+
+```bash
+npm run seed:run:relational
+```
+
+### Factory and Faker (TypeORM)
+
+1. Install faker:
+
+ ```bash
+ npm i --save-dev @faker-js/faker
+ ```
+
+1. Create `src/database/seeds/relational/user/user.factory.ts`:
+
+ ```ts
+ import { faker } from '@faker-js/faker';
+ import { RoleEnum } from '../../../../roles/roles.enum';
+ import { StatusEnum } from '../../../../statuses/statuses.enum';
+ import { Injectable } from '@nestjs/common';
+ import { InjectRepository } from '@nestjs/typeorm';
+ import { Repository } from 'typeorm';
+ import { RoleEntity } from '../../../../roles/infrastructure/persistence/relational/entities/role.entity';
+ import { UserEntity } from '../../../../users/infrastructure/persistence/relational/entities/user.entity';
+ import { StatusEntity } from '../../../../statuses/infrastructure/persistence/relational/entities/status.entity';
+
+ @Injectable()
+ export class UserFactory {
+ constructor(
+ @InjectRepository(UserEntity)
+ private repositoryUser: Repository,
+ @InjectRepository(RoleEntity)
+ private repositoryRole: Repository,
+ @InjectRepository(StatusEntity)
+ private repositoryStatus: Repository,
+ ) {}
+
+ createRandomUser() {
+ // Need for saving "this" context
+ return () => {
+ return this.repositoryUser.create({
+ firstName: faker.person.firstName(),
+ lastName: faker.person.lastName(),
+ email: faker.internet.email(),
+ password: faker.internet.password(),
+ role: this.repositoryRole.create({
+ id: RoleEnum.user,
+ name: 'User',
+ }),
+ status: this.repositoryStatus.create({
+ id: StatusEnum.active,
+ name: 'Active',
+ }),
+ });
+ };
+ }
+ }
+ ```
+
+1. Make changes in `src/database/seeds/relational/user/user-seed.service.ts`:
+
+ ```ts
+ // Some code here...
+ import { UserFactory } from './user.factory';
+ import { faker } from '@faker-js/faker';
+
+ @Injectable()
+ export class UserSeedService {
+ constructor(
+ // Some code here...
+ private userFactory: UserFactory,
+ ) {}
+
+ async run() {
+ // Some code here...
+
+ await this.repository.save(
+ faker.helpers.multiple(this.userFactory.createRandomUser(), {
+ count: 5,
+ }),
+ );
+ }
+ }
+ ```
+
+1. Make changes in `src/database/seeds/relational/user/user-seed.module.ts`:
+
+ ```ts
+ import { Module } from '@nestjs/common';
+ import { TypeOrmModule } from '@nestjs/typeorm';
+
+ import { UserSeedService } from './user-seed.service';
+ import { UserFactory } from './user.factory';
+
+ import { UserEntity } from '../../../../users/infrastructure/persistence/relational/entities/user.entity';
+ import { RoleEntity } from '../../../../roles/infrastructure/persistence/relational/entities/role.entity';
+ import { StatusEntity } from '../../../../statuses/infrastructure/persistence/relational/entities/status.entity';
+
+ @Module({
+ imports: [TypeOrmModule.forFeature([UserEntity, Role, Status])],
+ providers: [UserSeedService, UserFactory],
+ exports: [UserSeedService, UserFactory],
+ })
+ export class UserSeedModule {}
+
+ ```
+
+1. Run seed:
+
+ ```bash
+ npm run seed:run
+ ```
+
+---
+
+## Seeding (Mongoose)
+
+### Creating seeds (Mongoose)
+
+1. Create seed file with `npm run seed:create:document -- --name=Post`. Where `Post` is name of entity.
+1. Go to `src/database/seeds/document/post/post-seed.service.ts`.
+1. In `run` method extend your logic.
+1. Run [npm run seed:run:document](#run-seed-mongoose)
+
+### Run seed (Mongoose)
+
+```bash
+npm run seed:run:document
+```
+
+---
+
+## Performance optimization (PostgreSQL + TypeORM)
+
+### Indexes and Foreign Keys
+
+Don't forget to create `indexes` on the Foreign Keys (FK) columns (if needed), because by default PostgreSQL [does not automatically add indexes to FK](https://stackoverflow.com/a/970605/18140714).
+
+### Max connections
+
+Set the optimal number of [max connections](https://node-postgres.com/apis/pool) to database for your application in `/.env`:
+
+```txt
+DATABASE_MAX_CONNECTIONS=100
+```
+
+You can think of this parameter as how many concurrent database connections your application can handle.
+
+## Performance optimization (MongoDB + Mongoose)
+
+### Design schema
+
+Designing schema for MongoDB is completely different from designing schema for relational databases. For best performance, you should design your schema according to:
+
+1. [MongoDB Schema Design Anti-Patterns](https://www.mongodb.com/developer/products/mongodb/schema-design-anti-pattern-massive-arrays)
+1. [MongoDB Schema Design Best Practices](https://www.mongodb.com/developer/products/mongodb/mongodb-schema-design-best-practices/)
+
+---
+
+Previous: [Architecture](architecture.md)
+
+Next: [Auth](auth.md)
diff --git a/docs/file-uploading.md b/docs/file-uploading.md
new file mode 100644
index 0000000..922684f
--- /dev/null
+++ b/docs/file-uploading.md
@@ -0,0 +1,183 @@
+# File uploading
+
+---
+
+## Table of Contents
+
+- [Drivers support](#drivers-support)
+- [Uploading and attach file flow for `local` driver](#uploading-and-attach-file-flow-for-local-driver)
+ - [An example of uploading an avatar to a user profile (local)](#an-example-of-uploading-an-avatar-to-a-user-profile-local)
+ - [Video example](#video-example)
+- [Uploading and attach file flow for `s3` driver](#uploading-and-attach-file-flow-for-s3-driver)
+ - [Configuration for `s3` driver](#configuration-for-s3-driver)
+ - [An example of uploading an avatar to a user profile (S3)](#an-example-of-uploading-an-avatar-to-a-user-profile-s3)
+- [Uploading and attach file flow for `s3-presigned` driver](#uploading-and-attach-file-flow-for-s3-presigned-driver)
+ - [Configuration for `s3-presigned` driver](#configuration-for-s3-presigned-driver)
+ - [An example of uploading an avatar to a user profile (S3 Presigned URL)](#an-example-of-uploading-an-avatar-to-a-user-profile-s3-presigned-url)
+- [How to delete files?](#how-to-delete-files)
+
+---
+
+## Drivers support
+
+Out-of-box template supports the following drivers: `local`, `s3`, and `s3-presigned`. You can set it in the `.env` file, variable `FILE_DRIVER`. If you want to use another service for storing files, you can extend it.
+
+> For production we recommend using the "s3-presigned" driver to offload your server.
+
+---
+
+## Uploading and attach file flow for `local` driver
+
+Endpoint `/api/v1/files/upload` is used for uploading files, which returns `File` entity with `id` and `path`. After receiving `File` entity you can attach this to another entity.
+
+### An example of uploading an avatar to a user profile (local)
+
+```mermaid
+sequenceDiagram
+ participant A as Fronted App
+ participant B as Backend App
+
+ A->>B: Upload file via POST /api/v1/files/upload
+ B->>A: Receive File entity with "id" and "path" properties
+ note left of A: Attach File entity to User entity
+ A->>B: Update user via PATCH /api/v1/auth/me
+```
+
+### Video example
+
+
+
+## Uploading and attach file flow for `s3` driver
+
+Endpoint `/api/v1/files/upload` is used for uploading files, which returns `File` entity with `id` and `path`. After receiving `File` entity you can attach this to another entity.
+
+### Configuration for `s3` driver
+
+1. Open https://s3.console.aws.amazon.com/s3/buckets
+1. Click "Create bucket"
+1. Create bucket (for example, `your-unique-bucket-name`)
+1. Open your bucket
+1. Click "Permissions" tab
+1. Find "Cross-origin resource sharing (CORS)" section
+1. Click "Edit"
+1. Paste the following configuration
+
+ ```json
+ [
+ {
+ "AllowedHeaders": ["*"],
+ "AllowedMethods": ["GET"],
+ "AllowedOrigins": ["*"],
+ "ExposeHeaders": []
+ }
+ ]
+ ```
+
+1. Click "Save changes"
+1. Update `.env` file with the following variables:
+
+ ```dotenv
+ FILE_DRIVER=s3
+ ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
+ SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY
+ AWS_S3_REGION=YOUR_AWS_S3_REGION
+ AWS_DEFAULT_S3_BUCKET=YOUR_AWS_DEFAULT_S3_BUCKET
+ ```
+
+### An example of uploading an avatar to a user profile (S3)
+
+```mermaid
+sequenceDiagram
+ participant A as Fronted App
+ participant B as Backend App
+ participant C as AWS S3
+
+ A->>B: Upload file via POST /api/v1/files/upload
+ B->>C: Upload file to S3
+ B->>A: Receive File entity with "id" and "path" properties
+ note left of A: Attach File entity to User entity
+ A->>B: Update user via PATCH /api/v1/auth/me
+```
+
+## Uploading and attach file flow for `s3-presigned` driver
+
+Endpoint `/api/v1/files/upload` is used for uploading files. In this case `/api/v1/files/upload` receives only `fileName` property (without binary file), and returns the `presigned URL` and `File` entity with `id` and `path`. After receiving the `presigned URL` and `File` entity you need to upload your file to the `presigned URL` and after that attach `File` to another entity.
+
+### Configuration for `s3-presigned` driver
+
+1. Open https://s3.console.aws.amazon.com/s3/buckets
+1. Click "Create bucket"
+1. Create bucket (for example, `your-unique-bucket-name`)
+1. Open your bucket
+1. Click "Permissions" tab
+1. Find "Cross-origin resource sharing (CORS)" section
+1. Click "Edit"
+1. Paste the following configuration
+
+ ```json
+ [
+ {
+ "AllowedHeaders": ["*"],
+ "AllowedMethods": ["GET", "PUT"],
+ "AllowedOrigins": ["*"],
+ "ExposeHeaders": []
+ }
+ ]
+ ```
+
+ For production we recommend to use more strict configuration:
+
+ ```json
+ [
+ {
+ "AllowedHeaders": ["*"],
+ "AllowedMethods": ["PUT"],
+ "AllowedOrigins": ["https://your-domain.com"],
+ "ExposeHeaders": []
+ },
+ {
+ "AllowedHeaders": ["*"],
+ "AllowedMethods": ["GET"],
+ "AllowedOrigins": ["*"],
+ "ExposeHeaders": []
+ }
+ ]
+ ```
+
+1. Click "Save changes"
+1. Update `.env` file with the following variables:
+
+ ```dotenv
+ FILE_DRIVER=s3-presigned
+ ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
+ SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY
+ AWS_S3_REGION=YOUR_AWS_S3_REGION
+ AWS_DEFAULT_S3_BUCKET=YOUR_AWS_DEFAULT_S3_BUCKET
+ ```
+
+### An example of uploading an avatar to a user profile (S3 Presigned URL)
+
+```mermaid
+sequenceDiagram
+ participant C as AWS S3
+ participant A as Fronted App
+
+ participant B as Backend App
+
+ A->>B: Send file name (not binary file) via POST /api/v1/files/upload
+ note right of B: Generate presigned URL
+ B->>A: Receive presigned URL and File entity with "id" and "path" properties
+ A->>C: Upload file to S3 via presigned URL
+ note right of A: Attach File entity to User entity
+ A->>B: Update user via PATCH /api/v1/auth/me
+```
+
+## How to delete files?
+
+We prefer not to delete files, as this may have negative experience during restoring data. Also for this reason we also use [Soft-Delete](https://orkhan.gitbook.io/typeorm/docs/delete-query-builder#soft-delete) approach in database. However, if you need to delete files you can create your own handler, cronjob, etc.
+
+---
+
+Previous: [Serialization](serialization.md)
+
+Next: [Tests](tests.md)
diff --git a/docs/installing-and-running.md b/docs/installing-and-running.md
new file mode 100644
index 0000000..fab5c2e
--- /dev/null
+++ b/docs/installing-and-running.md
@@ -0,0 +1,215 @@
+# Installation
+
+NestJS Template supports [TypeORM](https://www.npmjs.com/package/typeorm) and [Mongoose](https://www.npmjs.com/package/mongoose) for working with databases. By default, TypeORM uses [PostgreSQL](https://www.postgresql.org/) as the main database, but you can use any relational database.
+
+Switching between TypeORM and Mongoose is implemented based on the [Dependency Inversion Principle](https://trilon.io/blog/dependency-inversion-principle) (DIP). This makes it easy to choose the right database for your application architecture.
+
+---
+
+## Table of Contents
+
+- [Comfortable development (PostgreSQL + TypeORM)](#comfortable-development-postgresql--typeorm)
+- [Comfortable development (MongoDB + Mongoose)](#comfortable-development-mongodb--mongoose)
+- [Quick run (PostgreSQL + TypeORM)](#quick-run-postgresql--typeorm)
+ - [Video guideline](#video-guideline)
+- [Quick run (MongoDB + Mongoose)](#quick-run-mongodb--mongoose)
+- [Links](#links)
+
+---
+
+## Comfortable development (PostgreSQL + TypeORM)
+
+1. Clone repository
+
+ ```bash
+ git clone --depth 1 https://github.com/khulnasoft/nestjs-template.git my-app
+ ```
+
+1. Go to folder, and copy `env-example-relational` as `.env`.
+
+ ```bash
+ cd my-app/
+ cp env-example-relational .env
+ ```
+
+1. Change `DATABASE_HOST=postgres` to `DATABASE_HOST=localhost`
+
+ Change `MAIL_HOST=maildev` to `MAIL_HOST=localhost`
+
+1. Run additional container:
+
+ ```bash
+ docker compose up -d postgres adminer maildev
+ ```
+
+1. Install dependency
+
+ ```bash
+ npm install
+ ```
+
+1. Run app configuration
+
+ > You should run this command only the first time on initialization of your project, all next time skip it.
+
+ ```bash
+ npm run app:config
+ ```
+
+1. Run migrations
+
+ ```bash
+ npm run migration:run
+ ```
+
+1. Run seeds
+
+ ```bash
+ npm run seed:run:relational
+ ```
+
+1. Run app in dev mode
+
+ ```bash
+ npm run start:dev
+ ```
+
+1. Open
+
+---
+
+## Comfortable development (MongoDB + Mongoose)
+
+1. Clone repository
+
+ ```bash
+ git clone --depth 1 https://github.com/khulnasoft/nestjs-template.git my-app
+ ```
+
+1. Go to folder, and copy `env-example-document` as `.env`.
+
+ ```bash
+ cd my-app/
+ cp env-example-document .env
+ ```
+
+1. Change `DATABASE_URL=mongodb://mongo:27017` to `DATABASE_URL=mongodb://localhost:27017`
+
+1. Run additional container:
+
+ ```bash
+ docker compose -f docker-compose.document.yaml up -d mongo mongo-express maildev
+ ```
+
+1. Install dependency
+
+ ```bash
+ npm install
+ ```
+
+1. Run app configuration
+
+ > You should run this command only the first time on initialization of your project, all next time skip it.
+
+ ```bash
+ npm run app:config
+ ```
+
+1. Run seeds
+
+ ```bash
+ npm run seed:run:document
+ ```
+
+1. Run app in dev mode
+
+ ```bash
+ npm run start:dev
+ ```
+
+1. Open
+
+---
+
+## Quick run (PostgreSQL + TypeORM)
+
+If you want quick run your app, you can use following commands:
+
+1. Clone repository
+
+ ```bash
+ git clone --depth 1 https://github.com/khulnasoft/nestjs-template.git my-app
+ ```
+
+1. Go to folder, and copy `env-example-relational` as `.env`.
+
+ ```bash
+ cd my-app/
+ cp env-example-relational .env
+ ```
+
+1. Run containers
+
+ ```bash
+ docker compose up -d
+ ```
+
+1. For check status run
+
+ ```bash
+ docker compose logs
+ ```
+
+1. Open
+
+### Video guideline
+
+
+
+---
+
+## Quick run (MongoDB + Mongoose)
+
+If you want quick run your app, you can use following commands:
+
+1. Clone repository
+
+ ```bash
+ git clone --depth 1 https://github.com/khulnasoft/nestjs-template.git my-app
+ ```
+
+1. Go to folder, and copy `env-example-document` as `.env`.
+
+ ```bash
+ cd my-app/
+ cp env-example-document .env
+ ```
+
+1. Run containers
+
+ ```bash
+ docker compose -f docker-compose.document.yaml up -d
+ ```
+
+1. For check status run
+
+ ```bash
+ docker compose -f docker-compose.document.yaml logs
+ ```
+
+1. Open
+
+---
+
+## Links
+
+- Swagger (API docs):
+- Adminer (client for DB):
+- MongoDB Express (client for DB):
+- Maildev:
+
+---
+
+Previous: [Introduction](introduction.md)
+
+Next: [Architecture](architecture.md)
diff --git a/docs/introduction.md b/docs/introduction.md
new file mode 100644
index 0000000..2e7df39
--- /dev/null
+++ b/docs/introduction.md
@@ -0,0 +1,29 @@
+# Introduction
+
+## Online demo
+
+Demo:
+
+Frontend (React, Next.js):
+
+## Features
+
+- [x] Database. Support [TypeORM](https://www.npmjs.com/package/typeorm) and [Mongoose](https://www.npmjs.com/package/mongoose).
+- [x] Seeding.
+- [x] Config Service ([@nestjs/config](https://www.npmjs.com/package/@nestjs/config)).
+- [x] Mailing ([nodemailer](https://www.npmjs.com/package/nodemailer)).
+- [x] Sign in and sign up via email.
+- [x] Social sign in (Apple, Facebook, Google, Twitter).
+- [x] Admin and User roles.
+- [x] Internationalization/Translations (I18N) ([nestjs-i18n](https://www.npmjs.com/package/nestjs-i18n)).
+- [x] File uploads. Support local and Amazon S3 drivers.
+- [x] Swagger.
+- [x] Support E2E and units tests.
+- [x] Docker.
+- [x] CI (Github Actions).
+
+---
+
+Previous: [Main](readme.md)
+
+Next: [Installing and Running](installing-and-running.md)
diff --git a/docs/readme.md b/docs/readme.md
new file mode 100644
index 0000000..51b1f0b
--- /dev/null
+++ b/docs/readme.md
@@ -0,0 +1,16 @@
+# NestJS Template Documentation
+
+---
+
+## Table of Contents
+
+- [Introduction](introduction.md)
+- [Installing and Running](installing-and-running.md)
+- [Architecture](architecture.md)
+- [Working with database](database.md)
+- [Auth](auth.md)
+- [Serialization](serialization.md)
+- [File uploading](file-uploading.md)
+- [Tests](tests.md)
+- [Benchmarking](benchmarking.md)
+- [Automatic update of dependencies](automatic-update-dependencies.md)
diff --git a/docs/serialization.md b/docs/serialization.md
new file mode 100644
index 0000000..63a0b9c
--- /dev/null
+++ b/docs/serialization.md
@@ -0,0 +1,94 @@
+# Serialization
+
+For serialization template use [class-transformer](https://www.npmjs.com/package/class-transformer) and global interceptor `ClassSerializerInterceptor`.
+
+---
+
+## Table of Contents
+
+- [Hide private property](#hide-private-property)
+- [Show private property for admins](#show-private-property-for-admins)
+
+---
+
+## Hide private property
+
+If you need to hide some property in the entity you can use `@Exclude({ toPlainOnly: true })` on the column.
+
+```ts
+// /src/users/entities/user.entity.ts
+
+import { Exclude } from 'class-transformer';
+
+@Entity()
+export class User extends EntityRelationalHelper {
+ // Some code here...
+
+ @Column({ nullable: true })
+ @Exclude({ toPlainOnly: true })
+ password: string;
+
+ // Some code here...
+}
+```
+
+## Show private property for admins
+
+1. Create a controller that returns data only for admin and add `@SerializeOptions({ groups: ['admin'] })` to method:
+
+ ```ts
+ // /src/users/users.controller.ts
+
+ // Some code here...
+
+ @ApiBearerAuth()
+ @Roles(RoleEnum.admin)
+ @UseGuards(AuthGuard('jwt'), RolesGuard)
+ @Controller({
+ path: 'users',
+ version: '1',
+ })
+ export class UsersController {
+ constructor(private readonly usersService: UsersService) {}
+
+ // Some code here...
+
+ @SerializeOptions({
+ groups: ['admin'],
+ })
+ @Get(':id')
+ @HttpCode(HttpStatus.OK)
+ findOne(@Param('id') id: string) {
+ return this.usersService.findOne({ id: +id });
+ }
+
+ // Some code here...
+ }
+ ```
+
+1. In the entity add `@Expose({ groups: ['admin'] })` to the column that should be exposed for admin:
+
+ ```ts
+ // /src/users/entities/user.entity.ts
+
+ // Some code here...
+
+ import { Expose } from 'class-transformer';
+
+ @Entity()
+ export class User extends EntityRelationalHelper {
+ // Some code here...
+
+ @Column({ unique: true, nullable: true })
+ @Expose({ groups: ['admin'] })
+ email: string | null;
+
+ // Some code here...
+ }
+ ```
+
+---
+
+Previous: [Auth](auth.md)
+
+Next: [File uploading](file-uploading.md)
diff --git a/docs/tests.md b/docs/tests.md
new file mode 100644
index 0000000..8c2faea
--- /dev/null
+++ b/docs/tests.md
@@ -0,0 +1,41 @@
+# Tests
+
+## Table of Contents
+
+- [Unit Tests](#unit-tests)
+- [E2E Tests](#e2e-tests)
+- [Tests in Docker](#tests-in-docker)
+ - [For relational database](#for-relational-database)
+ - [For document database](#for-document-database)
+
+## Unit Tests
+
+```bash
+npm run test
+```
+
+## E2E Tests
+
+```bash
+npm run test:e2e
+```
+
+## Tests in Docker
+
+### For relational database
+
+```bash
+npm run test:e2e:relational:docker
+```
+
+### For document database
+
+```bash
+npm run test:e2e:document:docker
+```
+
+---
+
+Previous: [File uploading](file-uploading.md)
+
+Next: [Benchmarking](benchmarking.md)
diff --git a/document.Dockerfile b/document.Dockerfile
new file mode 100644
index 0000000..835425b
--- /dev/null
+++ b/document.Dockerfile
@@ -0,0 +1,22 @@
+FROM node:20.12.2-alpine
+
+RUN apk add --no-cache bash
+RUN npm i -g @nestjs/cli typescript ts-node
+
+COPY package*.json /tmp/app/
+RUN cd /tmp/app && npm install
+
+COPY . /usr/src/app
+RUN cp -a /tmp/app/node_modules /usr/src/app
+COPY ./wait-for-it.sh /opt/wait-for-it.sh
+RUN chmod +x /opt/wait-for-it.sh
+COPY ./startup.document.dev.sh /opt/startup.document.dev.sh
+RUN chmod +x /opt/startup.document.dev.sh
+RUN sed -i 's/\r//g' /opt/wait-for-it.sh
+RUN sed -i 's/\r//g' /opt/startup.document.dev.sh
+
+WORKDIR /usr/src/app
+RUN if [ ! -f .env ]; then cp env-example-document .env; fi
+RUN npm run build
+
+CMD ["/opt/startup.document.dev.sh"]
diff --git a/document.e2e.Dockerfile b/document.e2e.Dockerfile
new file mode 100644
index 0000000..6716704
--- /dev/null
+++ b/document.e2e.Dockerfile
@@ -0,0 +1,22 @@
+FROM node:20.12.2-alpine
+
+RUN apk add --no-cache bash
+RUN npm i -g @nestjs/cli typescript ts-node
+
+COPY package*.json /tmp/app/
+RUN cd /tmp/app && npm install
+
+COPY . /usr/src/app
+RUN cp -a /tmp/app/node_modules /usr/src/app
+COPY ./wait-for-it.sh /opt/wait-for-it.sh
+RUN chmod +x /opt/wait-for-it.sh
+COPY ./startup.document.ci.sh /opt/startup.document.ci.sh
+RUN chmod +x /opt/startup.document.ci.sh
+RUN sed -i 's/\r//g' /opt/wait-for-it.sh
+RUN sed -i 's/\r//g' /opt/startup.document.ci.sh
+
+WORKDIR /usr/src/app
+RUN echo "" > .env
+RUN npm run build
+
+CMD ["/opt/startup.document.ci.sh"]
diff --git a/document.test.Dockerfile b/document.test.Dockerfile
new file mode 100644
index 0000000..d9957ef
--- /dev/null
+++ b/document.test.Dockerfile
@@ -0,0 +1,22 @@
+FROM node:20.12.2-alpine
+
+RUN apk add --no-cache bash
+RUN npm i -g @nestjs/cli typescript ts-node
+
+COPY package*.json /tmp/app/
+RUN cd /tmp/app && npm install
+
+COPY . /usr/src/app
+
+COPY ./wait-for-it.sh /opt/wait-for-it.sh
+RUN chmod +x /opt/wait-for-it.sh
+COPY ./startup.document.test.sh /opt/startup.document.test.sh
+RUN chmod +x /opt/startup.document.test.sh
+RUN sed -i 's/\r//g' /opt/wait-for-it.sh
+RUN sed -i 's/\r//g' /opt/startup.document.test.sh
+
+WORKDIR /usr/src/app
+
+RUN echo "" > .env
+
+CMD ["/opt/startup.document.test.sh"]
diff --git a/env-example-document b/env-example-document
new file mode 100644
index 0000000..4b04d00
--- /dev/null
+++ b/env-example-document
@@ -0,0 +1,55 @@
+NODE_ENV=development
+APP_PORT=3000
+APP_NAME="NestJS API"
+API_PREFIX=api
+APP_FALLBACK_LANGUAGE=en
+APP_HEADER_LANGUAGE=x-custom-lang
+FRONTEND_DOMAIN=http://localhost:3000
+BACKEND_DOMAIN=http://localhost:3000
+
+DATABASE_TYPE=mongodb
+DATABASE_PORT=27017
+DATABASE_USERNAME=root
+DATABASE_PASSWORD=secret
+DATABASE_NAME=api
+DATABASE_URL=mongodb://mongo:27017
+
+# Support "local", "s3", "s3-presigned"
+FILE_DRIVER=local
+ACCESS_KEY_ID=
+SECRET_ACCESS_KEY=
+AWS_S3_REGION=
+AWS_DEFAULT_S3_BUCKET=
+
+MAIL_HOST=maildev
+MAIL_PORT=1025
+MAIL_USER=
+MAIL_PASSWORD=
+MAIL_IGNORE_TLS=true
+MAIL_SECURE=false
+MAIL_REQUIRE_TLS=false
+MAIL_DEFAULT_EMAIL=noreply@example.com
+MAIL_DEFAULT_NAME=Api
+MAIL_CLIENT_PORT=1080
+
+AUTH_JWT_SECRET=secret
+AUTH_JWT_TOKEN_EXPIRES_IN=15m
+AUTH_REFRESH_SECRET=secret_for_refresh
+AUTH_REFRESH_TOKEN_EXPIRES_IN=3650d
+AUTH_FORGOT_SECRET=secret_for_forgot
+AUTH_FORGOT_TOKEN_EXPIRES_IN=30m
+AUTH_CONFIRM_EMAIL_SECRET=secret_for_confirm_email
+AUTH_CONFIRM_EMAIL_TOKEN_EXPIRES_IN=1d
+
+FACEBOOK_APP_ID=
+FACEBOOK_APP_SECRET=
+
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+
+APPLE_APP_AUDIENCE=[]
+
+TWITTER_CONSUMER_KEY=
+TWITTER_CONSUMER_SECRET=
+
+WORKER_HOST=redis://redis:6379/1
diff --git a/env-example-relational b/env-example-relational
new file mode 100644
index 0000000..792d7ec
--- /dev/null
+++ b/env-example-relational
@@ -0,0 +1,63 @@
+NODE_ENV=development
+APP_PORT=3000
+APP_NAME="NestJS API"
+API_PREFIX=api
+APP_FALLBACK_LANGUAGE=en
+APP_HEADER_LANGUAGE=x-custom-lang
+FRONTEND_DOMAIN=http://localhost:3000
+BACKEND_DOMAIN=http://localhost:3000
+
+DATABASE_TYPE=postgres
+DATABASE_HOST=postgres
+DATABASE_PORT=5432
+DATABASE_USERNAME=root
+DATABASE_PASSWORD=secret
+DATABASE_NAME=api
+DATABASE_SYNCHRONIZE=false
+DATABASE_MAX_CONNECTIONS=100
+DATABASE_SSL_ENABLED=false
+DATABASE_REJECT_UNAUTHORIZED=false
+DATABASE_CA=
+DATABASE_KEY=
+DATABASE_CERT=
+DATABASE_URL=
+
+# Support "local", "s3", "s3-presigned"
+FILE_DRIVER=local
+ACCESS_KEY_ID=
+SECRET_ACCESS_KEY=
+AWS_S3_REGION=
+AWS_DEFAULT_S3_BUCKET=
+
+MAIL_HOST=maildev
+MAIL_PORT=1025
+MAIL_USER=
+MAIL_PASSWORD=
+MAIL_IGNORE_TLS=true
+MAIL_SECURE=false
+MAIL_REQUIRE_TLS=false
+MAIL_DEFAULT_EMAIL=noreply@example.com
+MAIL_DEFAULT_NAME=Api
+MAIL_CLIENT_PORT=1080
+
+AUTH_JWT_SECRET=secret
+AUTH_JWT_TOKEN_EXPIRES_IN=15m
+AUTH_REFRESH_SECRET=secret_for_refresh
+AUTH_REFRESH_TOKEN_EXPIRES_IN=3650d
+AUTH_FORGOT_SECRET=secret_for_forgot
+AUTH_FORGOT_TOKEN_EXPIRES_IN=30m
+AUTH_CONFIRM_EMAIL_SECRET=secret_for_confirm_email
+AUTH_CONFIRM_EMAIL_TOKEN_EXPIRES_IN=1d
+
+FACEBOOK_APP_ID=
+FACEBOOK_APP_SECRET=
+
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+
+APPLE_APP_AUDIENCE=[]
+
+TWITTER_CONSUMER_KEY=
+TWITTER_CONSUMER_SECRET=
+
+WORKER_HOST=redis://redis:6379/1
diff --git a/maildev.Dockerfile b/maildev.Dockerfile
new file mode 100644
index 0000000..bb589e1
--- /dev/null
+++ b/maildev.Dockerfile
@@ -0,0 +1,5 @@
+FROM node:20.12.2-alpine
+
+RUN npm i -g maildev@2.0.5
+
+CMD maildev
diff --git a/nest-cli.json b/nest-cli.json
index 56167b3..960099e 100644
--- a/nest-cli.json
+++ b/nest-cli.json
@@ -1,4 +1,8 @@
{
+ "$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
- "sourceRoot": "src"
+ "sourceRoot": "src",
+ "compilerOptions": {
+ "assets": [{ "include": "i18n/**/*", "watchAssets": true }]
+ }
}
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 1732892..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,9446 +0,0 @@
-{
- "name": "nestjs-template",
- "version": "0.0.1",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
- "requires": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "@angular-devkit/core": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.6.tgz",
- "integrity": "sha512-3dA0Z6sIIxCDjZS/DucgmIKti7EZ/LgHoHgCO72Q50H5ZXbUSNBz5wGl5hVq2+gzrnFgU/0u40MIs6eptk30ZA==",
- "dev": true,
- "requires": {
- "ajv": "6.12.6",
- "fast-json-stable-stringify": "2.1.0",
- "magic-string": "0.25.7",
- "rxjs": "6.6.3",
- "source-map": "0.7.3"
- },
- "dependencies": {
- "rxjs": {
- "version": "6.6.3",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
- "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
- "dev": true,
- "requires": {
- "tslib": "^1.9.0"
- }
- },
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
- }
- }
- },
- "@angular-devkit/schematics": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.2.6.tgz",
- "integrity": "sha512-bhi2+5xtVAjtr3bsXKT8pnoBamQrArd/Y20ueA4Od7cd38YT97nzTA1wyHBFG0vWd0HMyg42ZS0aycNBuOebaA==",
- "dev": true,
- "requires": {
- "@angular-devkit/core": "11.2.6",
- "ora": "5.3.0",
- "rxjs": "6.6.3"
- },
- "dependencies": {
- "ora": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz",
- "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==",
- "dev": true,
- "requires": {
- "bl": "^4.0.3",
- "chalk": "^4.1.0",
- "cli-cursor": "^3.1.0",
- "cli-spinners": "^2.5.0",
- "is-interactive": "^1.0.0",
- "log-symbols": "^4.0.0",
- "strip-ansi": "^6.0.0",
- "wcwidth": "^1.0.1"
- }
- },
- "rxjs": {
- "version": "6.6.3",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
- "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
- "dev": true,
- "requires": {
- "tslib": "^1.9.0"
- }
- },
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
- }
- }
- },
- "@angular-devkit/schematics-cli": {
- "version": "0.1102.6",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-0.1102.6.tgz",
- "integrity": "sha512-86PmafA9mYDeM08cNWHcJCEY1Yqo5aq/YaBzCak93luByDQ4Ao4Jqts9l/xBCZBGUdVrczCNzcdwr/Y/6JPPzA==",
- "dev": true,
- "requires": {
- "@angular-devkit/core": "11.2.6",
- "@angular-devkit/schematics": "11.2.6",
- "@schematics/schematics": "0.1102.6",
- "ansi-colors": "4.1.1",
- "inquirer": "7.3.3",
- "minimist": "1.2.5",
- "symbol-observable": "3.0.0"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
- "dev": true
- }
- }
- },
- "@babel/code-frame": {
- "version": "7.24.2",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
- "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.24.2",
- "picocolors": "^1.0.0"
- }
- },
- "@babel/compat-data": {
- "version": "7.24.4",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
- "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
- "dev": true
- },
- "@babel/core": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
- "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
- "dev": true,
- "requires": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.24.2",
- "@babel/generator": "^7.24.5",
- "@babel/helper-compilation-targets": "^7.23.6",
- "@babel/helper-module-transforms": "^7.24.5",
- "@babel/helpers": "^7.24.5",
- "@babel/parser": "^7.24.5",
- "@babel/template": "^7.24.0",
- "@babel/traverse": "^7.24.5",
- "@babel/types": "^7.24.5",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
- "dependencies": {
- "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
- },
- "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"
- }
- },
- "json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true
- }
- }
- },
- "@babel/generator": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz",
- "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.24.5",
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^2.5.1"
- }
- },
- "@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,
- "requires": {
- "@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"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true
- }
- }
- },
- "@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
- },
- "@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,
- "requires": {
- "@babel/template": "^7.22.15",
- "@babel/types": "^7.23.0"
- }
- },
- "@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,
- "requires": {
- "@babel/types": "^7.22.5"
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.24.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
- "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.24.0"
- }
- },
- "@babel/helper-module-transforms": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz",
- "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==",
- "dev": true,
- "requires": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-module-imports": "^7.24.3",
- "@babel/helper-simple-access": "^7.24.5",
- "@babel/helper-split-export-declaration": "^7.24.5",
- "@babel/helper-validator-identifier": "^7.24.5"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz",
- "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==",
- "dev": true
- },
- "@babel/helper-simple-access": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz",
- "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.24.5"
- }
- },
- "@babel/helper-split-export-declaration": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz",
- "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.24.5"
- }
- },
- "@babel/helper-string-parser": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
- "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
- "dev": true
- },
- "@babel/helper-validator-identifier": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
- "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==",
- "dev": true
- },
- "@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
- },
- "@babel/helpers": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz",
- "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.24.0",
- "@babel/traverse": "^7.24.5",
- "@babel/types": "^7.24.5"
- }
- },
- "@babel/highlight": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
- "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.24.5",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "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
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "@babel/parser": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
- "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
- "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-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/template": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
- "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.23.5",
- "@babel/parser": "^7.24.0",
- "@babel/types": "^7.24.0"
- }
- },
- "@babel/traverse": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz",
- "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.24.2",
- "@babel/generator": "^7.24.5",
- "@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.24.5",
- "@babel/parser": "^7.24.5",
- "@babel/types": "^7.24.5",
- "debug": "^4.3.1",
- "globals": "^11.1.0"
- },
- "dependencies": {
- "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"
- }
- },
- "globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
- }
- },
- "@babel/types": {
- "version": "7.24.5",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz",
- "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==",
- "dev": true,
- "requires": {
- "@babel/helper-string-parser": "^7.24.1",
- "@babel/helper-validator-identifier": "^7.24.5",
- "to-fast-properties": "^2.0.0"
- }
- },
- "@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
- },
- "@cnakazawa/watch": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
- "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==",
- "dev": true,
- "requires": {
- "exec-sh": "^0.3.2",
- "minimist": "^1.2.0"
- }
- },
- "@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": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz",
- "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "jest-message-util": "^26.6.2",
- "jest-util": "^26.6.2",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "@jest/core": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz",
- "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==",
- "dev": true,
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/reporters": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.4",
- "jest-changed-files": "^26.6.2",
- "jest-config": "^26.6.3",
- "jest-haste-map": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-resolve": "^26.6.2",
- "jest-resolve-dependencies": "^26.6.3",
- "jest-runner": "^26.6.3",
- "jest-runtime": "^26.6.3",
- "jest-snapshot": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "jest-watcher": "^26.6.2",
- "micromatch": "^4.0.2",
- "p-each-series": "^2.1.0",
- "rimraf": "^3.0.0",
- "slash": "^3.0.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "@jest/environment": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz",
- "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "jest-mock": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "@jest/fake-timers": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz",
- "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "@sinonjs/fake-timers": "^6.0.1",
- "@types/node": "*",
- "jest-message-util": "^26.6.2",
- "jest-mock": "^26.6.2",
- "jest-util": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "@jest/globals": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz",
- "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==",
- "dev": true,
- "requires": {
- "@jest/environment": "^26.6.2",
- "@jest/types": "^26.6.2",
- "expect": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "@jest/reporters": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz",
- "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==",
- "dev": true,
- "requires": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.2",
- "graceful-fs": "^4.2.4",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^4.0.3",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.0.2",
- "jest-haste-map": "^26.6.2",
- "jest-resolve": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-worker": "^26.6.2",
- "node-notifier": "^8.0.0",
- "slash": "^3.0.0",
- "source-map": "^0.6.0",
- "string-length": "^4.0.1",
- "terminal-link": "^2.0.0",
- "v8-to-istanbul": "^7.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-worker": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
- "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^7.0.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "@jest/source-map": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz",
- "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==",
- "dev": true,
- "requires": {
- "callsites": "^3.0.0",
- "graceful-fs": "^4.2.4",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "@jest/test-result": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz",
- "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==",
- "dev": true,
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "collect-v8-coverage": "^1.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "@jest/test-sequencer": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz",
- "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==",
- "dev": true,
- "requires": {
- "@jest/test-result": "^26.6.2",
- "graceful-fs": "^4.2.4",
- "jest-haste-map": "^26.6.2",
- "jest-runner": "^26.6.3",
- "jest-runtime": "^26.6.3"
- }
- },
- "@jest/transform": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz",
- "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.1.0",
- "@jest/types": "^26.6.2",
- "babel-plugin-istanbul": "^6.0.0",
- "chalk": "^4.0.0",
- "convert-source-map": "^1.4.0",
- "fast-json-stable-stringify": "^2.0.0",
- "graceful-fs": "^4.2.4",
- "jest-haste-map": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-util": "^26.6.2",
- "micromatch": "^4.0.2",
- "pirates": "^4.0.1",
- "slash": "^3.0.0",
- "source-map": "^0.6.1",
- "write-file-atomic": "^3.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "@jest/types": {
- "version": "25.5.0",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz",
- "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^1.1.1",
- "@types/yargs": "^15.0.0",
- "chalk": "^3.0.0"
- },
- "dependencies": {
- "chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- }
- }
- },
- "@jridgewell/gen-mapping": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
- "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
- "dev": true,
- "requires": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true
- },
- "@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true
- },
- "@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
- "requires": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
- },
- "@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
- },
- "@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "requires": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "@nestjs/cli": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-7.6.0.tgz",
- "integrity": "sha512-lW1px2gSHkRoBpKSxzP6IJNQscRKs97OAaVyV46OAP6oUR996E0EPkIslIaa16kKLJ3SFOUeZo5xl5nYbqp43g==",
- "dev": true,
- "requires": {
- "@angular-devkit/core": "11.2.6",
- "@angular-devkit/schematics": "11.2.6",
- "@angular-devkit/schematics-cli": "0.1102.6",
- "@nestjs/schematics": "^7.3.0",
- "chalk": "3.0.0",
- "chokidar": "3.5.1",
- "cli-table3": "0.5.1",
- "commander": "4.1.1",
- "fork-ts-checker-webpack-plugin": "6.2.0",
- "inquirer": "7.3.3",
- "node-emoji": "1.10.0",
- "ora": "5.4.0",
- "os-name": "4.0.0",
- "rimraf": "3.0.2",
- "shelljs": "0.8.4",
- "tree-kill": "1.2.2",
- "tsconfig-paths": "3.9.0",
- "tsconfig-paths-webpack-plugin": "3.5.1",
- "typescript": "4.2.3",
- "webpack": "5.28.0",
- "webpack-node-externals": "2.5.2"
- },
- "dependencies": {
- "chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "tsconfig-paths": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
- "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
- "dev": true,
- "requires": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.1",
- "minimist": "^1.2.0",
- "strip-bom": "^3.0.0"
- }
- },
- "typescript": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
- "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
- "dev": true
- }
- }
- },
- "@nestjs/common": {
- "version": "7.6.18",
- "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-7.6.18.tgz",
- "integrity": "sha512-BUJQHNhWzwWOkS4Ryndzd4HTeRObcAWV2Fh+ermyo3q3xYQQzNoEWclJVL/wZec8AONELwIJ+PSpWI53VP0leg==",
- "requires": {
- "axios": "0.21.1",
- "iterare": "1.2.1",
- "tslib": "2.2.0",
- "uuid": "8.3.2"
- }
- },
- "@nestjs/core": {
- "version": "7.6.18",
- "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-7.6.18.tgz",
- "integrity": "sha512-CGu20OjIxgFDY7RJT5t1TDGL8wSlTSlbZEkn8U5OlICZEB3WIpi98G7ajJpnRWmEgW8S4aDJmRKGjT+Ntj5U4A==",
- "requires": {
- "@nuxtjs/opencollective": "0.3.2",
- "fast-safe-stringify": "2.0.7",
- "iterare": "1.2.1",
- "object-hash": "2.1.1",
- "path-to-regexp": "3.2.0",
- "tslib": "2.2.0",
- "uuid": "8.3.2"
- }
- },
- "@nestjs/platform-express": {
- "version": "7.6.18",
- "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-7.6.18.tgz",
- "integrity": "sha512-Dty2bBhsW7EInMRPS1pkXKJ3GBBusEj6fnEpb0UfkaT3E7asay9c64kCmZE+8hU430qQjY+fhBb1RNWWPnUiwQ==",
- "requires": {
- "body-parser": "1.19.0",
- "cors": "2.8.5",
- "express": "4.17.1",
- "multer": "1.4.2",
- "tslib": "2.2.0"
- }
- },
- "@nestjs/schematics": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-7.3.1.tgz",
- "integrity": "sha512-eyBjJstAjecpdzRuBLiqnwomwXIAEV3+kPkpaphOieRUM6nBhjnXCCl3Qf8Dul2QUQK4NOVPd8FFxWtGP5XNlg==",
- "dev": true,
- "requires": {
- "@angular-devkit/core": "11.2.4",
- "@angular-devkit/schematics": "11.2.4",
- "fs-extra": "9.1.0",
- "jsonc-parser": "3.0.0",
- "pluralize": "8.0.0"
- },
- "dependencies": {
- "@angular-devkit/core": {
- "version": "11.2.4",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.4.tgz",
- "integrity": "sha512-98mGDV4XtKWiQ/2D6yzvOHrnJovXchaAN9AjscAHd2an8Fkiq72d9m2wREpk+2J40NWTDB6J5iesTh3qbi8+CA==",
- "dev": true,
- "requires": {
- "ajv": "6.12.6",
- "fast-json-stable-stringify": "2.1.0",
- "magic-string": "0.25.7",
- "rxjs": "6.6.3",
- "source-map": "0.7.3"
- }
- },
- "@angular-devkit/schematics": {
- "version": "11.2.4",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.2.4.tgz",
- "integrity": "sha512-M9Ike1TYawOIHzenlZS1ufQbsS+Z11/doj5w/UrU0q2OEKc6U375t5qVGgKo3PLHHS8osb9aW9xYwBfVlKrryQ==",
- "dev": true,
- "requires": {
- "@angular-devkit/core": "11.2.4",
- "ora": "5.3.0",
- "rxjs": "6.6.3"
- }
- },
- "ora": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz",
- "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==",
- "dev": true,
- "requires": {
- "bl": "^4.0.3",
- "chalk": "^4.1.0",
- "cli-cursor": "^3.1.0",
- "cli-spinners": "^2.5.0",
- "is-interactive": "^1.0.0",
- "log-symbols": "^4.0.0",
- "strip-ansi": "^6.0.0",
- "wcwidth": "^1.0.1"
- }
- },
- "rxjs": {
- "version": "6.6.3",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
- "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
- "dev": true,
- "requires": {
- "tslib": "^1.9.0"
- }
- },
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
- }
- }
- },
- "@nestjs/testing": {
- "version": "7.6.18",
- "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-7.6.18.tgz",
- "integrity": "sha512-1AVk9vWZlPpx4CmzY6z9z0DHFgGCadfr01QdisGFAN740JwKqZWEqz12cVd+nsXDlYQPFRkp2ICBIS/6k1qZGQ==",
- "dev": true,
- "requires": {
- "optional": "0.1.4",
- "tslib": "2.2.0"
- }
- },
- "@nuxtjs/opencollective": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz",
- "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==",
- "requires": {
- "chalk": "^4.1.0",
- "consola": "^2.15.0",
- "node-fetch": "^2.6.1"
- }
- },
- "@schematics/schematics": {
- "version": "0.1102.6",
- "resolved": "https://registry.npmjs.org/@schematics/schematics/-/schematics-0.1102.6.tgz",
- "integrity": "sha512-x77kbJL/HqR4gx0tbt35VCOGLyMvB7jD/x7eB1njhQRF8E/xynEOk3i+7A5VmK67QP5NJxU8BQKlPkJ55tBDmg==",
- "dev": true,
- "requires": {
- "@angular-devkit/core": "11.2.6",
- "@angular-devkit/schematics": "11.2.6"
- }
- },
- "@sinonjs/commons": {
- "version": "1.8.6",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz",
- "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==",
- "dev": true,
- "requires": {
- "type-detect": "4.0.8"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
- "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@tootallnate/once": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
- "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
- "dev": true
- },
- "@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,
- "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.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,
- "requires": {
- "@babel/types": "^7.0.0"
- }
- },
- "@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,
- "requires": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "@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,
- "requires": {
- "@babel/types": "^7.20.7"
- }
- },
- "@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==",
- "dev": true,
- "requires": {
- "@types/connect": "*",
- "@types/node": "*"
- }
- },
- "@types/connect": {
- "version": "3.4.38",
- "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
- "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
- "dev": true,
- "requires": {
- "@types/node": "*"
- }
- },
- "@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
- },
- "@types/eslint": {
- "version": "8.56.10",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
- "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
- "dev": true,
- "requires": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
- "@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,
- "requires": {
- "@types/eslint": "*",
- "@types/estree": "*"
- }
- },
- "@types/eslint-visitor-keys": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
- "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
- "dev": true
- },
- "@types/estree": {
- "version": "0.0.46",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz",
- "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==",
- "dev": true
- },
- "@types/express": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
- "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
- "dev": true,
- "requires": {
- "@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.33",
- "@types/qs": "*",
- "@types/serve-static": "*"
- }
- },
- "@types/express-serve-static-core": {
- "version": "4.19.0",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz",
- "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "@types/qs": "*",
- "@types/range-parser": "*",
- "@types/send": "*"
- }
- },
- "@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,
- "requires": {
- "@types/node": "*"
- }
- },
- "@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==",
- "dev": true
- },
- "@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
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "@types/istanbul-reports": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz",
- "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "*",
- "@types/istanbul-lib-report": "*"
- }
- },
- "@types/jest": {
- "version": "26.0.10",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.10.tgz",
- "integrity": "sha512-i2m0oyh8w/Lum7wWK/YOZJakYF8Mx08UaKA1CtbmFeDquVhAEdA7znacsVSf2hJ1OQ/OfVMGN90pw/AtzF8s/Q==",
- "dev": true,
- "requires": {
- "jest-diff": "^25.2.1",
- "pretty-format": "^25.2.1"
- }
- },
- "@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/json5": {
- "version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
- "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
- "dev": true
- },
- "@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
- },
- "@types/mime": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
- "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
- "dev": true
- },
- "@types/node": {
- "version": "13.13.52",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
- "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==",
- "dev": true
- },
- "@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
- },
- "@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==",
- "dev": true
- },
- "@types/prettier": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
- "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==",
- "dev": true
- },
- "@types/qs": {
- "version": "6.9.15",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
- "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==",
- "dev": true
- },
- "@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==",
- "dev": true
- },
- "@types/send": {
- "version": "0.17.4",
- "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
- "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
- "dev": true,
- "requires": {
- "@types/mime": "^1",
- "@types/node": "*"
- }
- },
- "@types/serve-static": {
- "version": "1.15.7",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
- "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
- "dev": true,
- "requires": {
- "@types/http-errors": "*",
- "@types/node": "*",
- "@types/send": "*"
- }
- },
- "@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
- },
- "@types/superagent": {
- "version": "8.1.7",
- "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.7.tgz",
- "integrity": "sha512-NmIsd0Yj4DDhftfWvvAku482PZum4DBW7U51OvS8gvOkDDY0WT1jsVyDV3hK+vplrsYw8oDwi9QxOM7U68iwww==",
- "dev": true,
- "requires": {
- "@types/cookiejar": "^2.1.5",
- "@types/methods": "^1.1.4",
- "@types/node": "*"
- }
- },
- "@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,
- "requires": {
- "@types/superagent": "*"
- }
- },
- "@types/yargs": {
- "version": "15.0.19",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz",
- "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "@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
- },
- "@typescript-eslint/eslint-plugin": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.9.1.tgz",
- "integrity": "sha512-XIr+Mfv7i4paEdBf0JFdIl9/tVxyj+rlilWIfZ97Be0lZ7hPvUbS5iHt9Glc8kRI53dsr0PcAEudbf8rO2wGgg==",
- "dev": true,
- "requires": {
- "@typescript-eslint/experimental-utils": "3.9.1",
- "debug": "^4.1.1",
- "functional-red-black-tree": "^1.0.1",
- "regexpp": "^3.0.0",
- "semver": "^7.3.2",
- "tsutils": "^3.17.1"
- },
- "dependencies": {
- "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"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
- }
- },
- "@typescript-eslint/experimental-utils": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.9.1.tgz",
- "integrity": "sha512-lkiZ8iBBaYoyEKhCkkw4SAeatXyBq9Ece5bZXdLe1LWBUwTszGbmbiqmQbwWA8cSYDnjWXp9eDbXpf9Sn0hLAg==",
- "dev": true,
- "requires": {
- "@types/json-schema": "^7.0.3",
- "@typescript-eslint/types": "3.9.1",
- "@typescript-eslint/typescript-estree": "3.9.1",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^2.0.0"
- }
- },
- "@typescript-eslint/parser": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.9.1.tgz",
- "integrity": "sha512-y5QvPFUn4Vl4qM40lI+pNWhTcOWtpZAJ8pOEQ21fTTW4xTJkRplMjMRje7LYTXqVKKX9GJhcyweMz2+W1J5bMg==",
- "dev": true,
- "requires": {
- "@types/eslint-visitor-keys": "^1.0.0",
- "@typescript-eslint/experimental-utils": "3.9.1",
- "@typescript-eslint/types": "3.9.1",
- "@typescript-eslint/typescript-estree": "3.9.1",
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "@typescript-eslint/types": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.9.1.tgz",
- "integrity": "sha512-15JcTlNQE1BsYy5NBhctnEhEoctjXOjOK+Q+rk8ugC+WXU9rAcS2BYhoh6X4rOaXJEpIYDl+p7ix+A5U0BqPTw==",
- "dev": true
- },
- "@typescript-eslint/typescript-estree": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.9.1.tgz",
- "integrity": "sha512-IqM0gfGxOmIKPhiHW/iyAEXwSVqMmR2wJ9uXHNdFpqVvPaQ3dWg302vW127sBpAiqM9SfHhyS40NKLsoMpN2KA==",
- "dev": true,
- "requires": {
- "@typescript-eslint/types": "3.9.1",
- "@typescript-eslint/visitor-keys": "3.9.1",
- "debug": "^4.1.1",
- "glob": "^7.1.6",
- "is-glob": "^4.0.1",
- "lodash": "^4.17.15",
- "semver": "^7.3.2",
- "tsutils": "^3.17.1"
- },
- "dependencies": {
- "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"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
- }
- },
- "@typescript-eslint/visitor-keys": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.9.1.tgz",
- "integrity": "sha512-zxdtUjeoSh+prCpogswMwVUJfEFmCOjdzK9rpNjNBfm6EyPt99x3RrJoBOGZO23FCt0WPKUCOL5mb/9D5LjdwQ==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "@webassemblyjs/ast": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz",
- "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==",
- "dev": true,
- "requires": {
- "@webassemblyjs/helper-numbers": "1.11.0",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.0"
- }
- },
- "@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz",
- "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==",
- "dev": true
- },
- "@webassemblyjs/helper-api-error": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz",
- "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==",
- "dev": true
- },
- "@webassemblyjs/helper-buffer": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz",
- "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==",
- "dev": true
- },
- "@webassemblyjs/helper-numbers": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz",
- "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==",
- "dev": true,
- "requires": {
- "@webassemblyjs/floating-point-hex-parser": "1.11.0",
- "@webassemblyjs/helper-api-error": "1.11.0",
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz",
- "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==",
- "dev": true
- },
- "@webassemblyjs/helper-wasm-section": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz",
- "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.0",
- "@webassemblyjs/helper-buffer": "1.11.0",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.0",
- "@webassemblyjs/wasm-gen": "1.11.0"
- }
- },
- "@webassemblyjs/ieee754": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz",
- "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==",
- "dev": true,
- "requires": {
- "@xtuc/ieee754": "^1.2.0"
- }
- },
- "@webassemblyjs/leb128": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz",
- "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==",
- "dev": true,
- "requires": {
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/utf8": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz",
- "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==",
- "dev": true
- },
- "@webassemblyjs/wasm-edit": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz",
- "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.0",
- "@webassemblyjs/helper-buffer": "1.11.0",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.0",
- "@webassemblyjs/helper-wasm-section": "1.11.0",
- "@webassemblyjs/wasm-gen": "1.11.0",
- "@webassemblyjs/wasm-opt": "1.11.0",
- "@webassemblyjs/wasm-parser": "1.11.0",
- "@webassemblyjs/wast-printer": "1.11.0"
- }
- },
- "@webassemblyjs/wasm-gen": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz",
- "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.0",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.0",
- "@webassemblyjs/ieee754": "1.11.0",
- "@webassemblyjs/leb128": "1.11.0",
- "@webassemblyjs/utf8": "1.11.0"
- }
- },
- "@webassemblyjs/wasm-opt": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz",
- "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.0",
- "@webassemblyjs/helper-buffer": "1.11.0",
- "@webassemblyjs/wasm-gen": "1.11.0",
- "@webassemblyjs/wasm-parser": "1.11.0"
- }
- },
- "@webassemblyjs/wasm-parser": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz",
- "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.0",
- "@webassemblyjs/helper-api-error": "1.11.0",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.0",
- "@webassemblyjs/ieee754": "1.11.0",
- "@webassemblyjs/leb128": "1.11.0",
- "@webassemblyjs/utf8": "1.11.0"
- }
- },
- "@webassemblyjs/wast-printer": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz",
- "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.0",
- "@xtuc/long": "4.2.2"
- }
- },
- "@xtuc/ieee754": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
- "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
- "dev": true
- },
- "@xtuc/long": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
- "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
- "dev": true
- },
- "abab": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
- "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
- "dev": true
- },
- "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==",
- "requires": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- }
- },
- "acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "dev": true
- },
- "acorn-globals": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
- "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
- "dev": true,
- "requires": {
- "acorn": "^7.1.1",
- "acorn-walk": "^7.1.1"
- },
- "dependencies": {
- "acorn": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
- "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
- "dev": true
- }
- }
- },
- "acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true
- },
- "acorn-walk": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
- "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
- "dev": true
- },
- "agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "dev": true,
- "requires": {
- "debug": "4"
- },
- "dependencies": {
- "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"
- }
- },
- "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
- }
- }
- },
- "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"
- }
- },
- "ajv-keywords": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
- "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "dev": true
- },
- "ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
- "dev": true
- },
- "ansi-escapes": {
- "version": "4.3.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"
- }
- },
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "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"
- }
- },
- "append-field": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
- "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
- },
- "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"
- }
- },
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
- "dev": true
- },
- "arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
- "dev": true
- },
- "arr-union": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
- "dev": true
- },
- "array-buffer-byte-length": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
- "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.5",
- "is-array-buffer": "^3.0.4"
- }
- },
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
- },
- "array-includes": {
- "version": "3.1.8",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
- "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.4",
- "is-string": "^1.0.7"
- }
- },
- "array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
- "dev": true
- },
- "array.prototype.findlastindex": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
- "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "es-shim-unscopables": "^1.0.2"
- }
- },
- "array.prototype.flat": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
- "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "es-abstract": "^1.22.1",
- "es-shim-unscopables": "^1.0.0"
- }
- },
- "array.prototype.flatmap": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
- "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "es-abstract": "^1.22.1",
- "es-shim-unscopables": "^1.0.0"
- }
- },
- "arraybuffer.prototype.slice": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
- "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
- "dev": true,
- "requires": {
- "array-buffer-byte-length": "^1.0.1",
- "call-bind": "^1.0.5",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.22.3",
- "es-errors": "^1.2.1",
- "get-intrinsic": "^1.2.3",
- "is-array-buffer": "^3.0.4",
- "is-shared-array-buffer": "^1.0.2"
- }
- },
- "assign-symbols": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
- "dev": true
- },
- "astral-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
- "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
- "dev": true
- },
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "dev": true
- },
- "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
- },
- "atob": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
- "dev": true
- },
- "available-typed-arrays": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
- "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
- "dev": true,
- "requires": {
- "possible-typed-array-names": "^1.0.0"
- }
- },
- "axios": {
- "version": "0.21.1",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
- "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
- "requires": {
- "follow-redirects": "^1.10.0"
- }
- },
- "babel-jest": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
- "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==",
- "dev": true,
- "requires": {
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/babel__core": "^7.1.7",
- "babel-plugin-istanbul": "^6.0.0",
- "babel-preset-jest": "^26.6.2",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "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"
- },
- "dependencies": {
- "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"
- }
- },
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true
- }
- }
- },
- "babel-plugin-jest-hoist": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz",
- "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.3.3",
- "@babel/types": "^7.3.3",
- "@types/babel__core": "^7.0.0",
- "@types/babel__traverse": "^7.0.6"
- }
- },
- "babel-preset-current-node-syntax": {
- "version": "1.0.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": "26.6.2",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz",
- "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==",
- "dev": true,
- "requires": {
- "babel-plugin-jest-hoist": "^26.6.2",
- "babel-preset-current-node-syntax": "^1.0.0"
- }
- },
- "balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "dev": true,
- "requires": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- }
- }
- }
- },
- "base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "dev": true
- },
- "big.js": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
- "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
- "dev": true
- },
- "binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true
- },
- "bl": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
- "dev": true,
- "requires": {
- "buffer": "^5.5.0",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- },
- "readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- },
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true
- },
- "string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.2.0"
- }
- }
- }
- },
- "body-parser": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
- "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
- "requires": {
- "bytes": "3.1.0",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "http-errors": "1.7.2",
- "iconv-lite": "0.4.24",
- "on-finished": "~2.3.0",
- "qs": "6.7.0",
- "raw-body": "2.4.0",
- "type-is": "~1.6.17"
- }
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "browser-process-hrtime": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
- "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
- "dev": true
- },
- "browserslist": {
- "version": "4.23.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
- "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001587",
- "electron-to-chromium": "^1.4.668",
- "node-releases": "^2.0.14",
- "update-browserslist-db": "^1.0.13"
- }
- },
- "bs-logger": {
- "version": "0.2.6",
- "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
- "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
- "dev": true,
- "requires": {
- "fast-json-stable-stringify": "2.x"
- }
- },
- "bser": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
- "dev": true,
- "requires": {
- "node-int64": "^0.4.0"
- }
- },
- "buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "dev": true,
- "requires": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
- "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=="
- },
- "busboy": {
- "version": "0.2.14",
- "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
- "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==",
- "requires": {
- "dicer": "0.2.5",
- "readable-stream": "1.1.x"
- }
- },
- "bytes": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
- "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
- },
- "cache-base": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
- "dev": true,
- "requires": {
- "collection-visit": "^1.0.0",
- "component-emitter": "^1.2.1",
- "get-value": "^2.0.6",
- "has-value": "^1.0.0",
- "isobject": "^3.0.1",
- "set-value": "^2.0.0",
- "to-object-path": "^0.3.0",
- "union-value": "^1.0.0",
- "unset-value": "^1.0.0"
- }
- },
- "call-bind": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
- "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
- "dev": true,
- "requires": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.1"
- }
- },
- "callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true
- },
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true
- },
- "caniuse-lite": {
- "version": "1.0.30001618",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz",
- "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==",
- "dev": true
- },
- "capture-exit": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
- "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==",
- "dev": true,
- "requires": {
- "rsvp": "^4.8.4"
- }
- },
- "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"
- }
- },
- "char-regex": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
- "dev": true
- },
- "chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "dev": true
- },
- "chokidar": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
- "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
- "dev": true,
- "requires": {
- "anymatch": "~3.1.1",
- "braces": "~3.0.2",
- "fsevents": "~2.3.1",
- "glob-parent": "~5.1.0",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.5.0"
- }
- },
- "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
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "cjs-module-lexer": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz",
- "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==",
- "dev": true
- },
- "class-utils": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
- "dev": true,
- "requires": {
- "arr-union": "^3.1.0",
- "define-property": "^0.2.5",
- "isobject": "^3.0.0",
- "static-extend": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- }
- }
- },
- "cli-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
- "dev": true,
- "requires": {
- "restore-cursor": "^3.1.0"
- }
- },
- "cli-spinners": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
- "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
- "dev": true
- },
- "cli-table3": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
- "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
- "dev": true,
- "requires": {
- "colors": "^1.1.2",
- "object-assign": "^4.1.0",
- "string-width": "^2.1.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "dev": true
- },
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "dev": true,
- "requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
- }
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- }
- }
- },
- "cli-width": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
- "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
- "dev": true
- },
- "cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dev": true,
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "clone": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
- "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
- "dev": true
- },
- "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.2",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
- "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
- "dev": true
- },
- "collection-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
- "dev": true,
- "requires": {
- "map-visit": "^1.0.0",
- "object-visit": "^1.0.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=="
- },
- "colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
- "dev": true,
- "optional": true
- },
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dev": true,
- "requires": {
- "delayed-stream": "~1.0.0"
- }
- },
- "commander": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
- "dev": true
- },
- "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
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
- },
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "requires": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- },
- "readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
- }
- },
- "consola": {
- "version": "2.15.3",
- "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
- "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw=="
- },
- "content-disposition": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
- "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
- "requires": {
- "safe-buffer": "5.1.2"
- }
- },
- "content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
- },
- "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
- },
- "cookie": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
- "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
- },
- "cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
- },
- "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
- },
- "copy-descriptor": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
- "dev": true
- },
- "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=="
- },
- "cors": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
- "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
- "requires": {
- "object-assign": "^4",
- "vary": "^1"
- }
- },
- "cosmiconfig": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "dev": true,
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.7.2"
- }
- },
- "cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "cssom": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
- "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==",
- "dev": true
- },
- "cssstyle": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
- "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
- "dev": true,
- "requires": {
- "cssom": "~0.3.6"
- },
- "dependencies": {
- "cssom": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
- "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
- "dev": true
- }
- }
- },
- "data-urls": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
- "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
- "dev": true,
- "requires": {
- "abab": "^2.0.3",
- "whatwg-mimetype": "^2.3.0",
- "whatwg-url": "^8.0.0"
- },
- "dependencies": {
- "tr46": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
- "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
- "dev": true,
- "requires": {
- "punycode": "^2.1.1"
- }
- },
- "webidl-conversions": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
- "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
- "dev": true
- },
- "whatwg-url": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
- "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
- "dev": true,
- "requires": {
- "lodash": "^4.7.0",
- "tr46": "^2.1.0",
- "webidl-conversions": "^6.1.0"
- }
- }
- }
- },
- "data-view-buffer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
- "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.6",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.1"
- }
- },
- "data-view-byte-length": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
- "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.1"
- }
- },
- "data-view-byte-offset": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
- "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.6",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.1"
- }
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "dev": true
- },
- "decimal.js": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
- "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
- "dev": true
- },
- "decode-uri-component": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
- "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
- "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.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true
- },
- "defaults": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
- "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
- "dev": true,
- "requires": {
- "clone": "^1.0.2"
- }
- },
- "define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
- "requires": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
- }
- },
- "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,
- "requires": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- }
- },
- "define-property": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.2",
- "isobject": "^3.0.1"
- },
- "dependencies": {
- "is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- }
- }
- }
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "dev": true
- },
- "depd": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
- },
- "destroy": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
- "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg=="
- },
- "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
- },
- "dicer": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
- "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==",
- "requires": {
- "readable-stream": "1.1.x",
- "streamsearch": "0.1.2"
- }
- },
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
- },
- "diff-sequences": {
- "version": "25.2.6",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz",
- "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==",
- "dev": true
- },
- "doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "domexception": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
- "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
- "dev": true,
- "requires": {
- "webidl-conversions": "^5.0.0"
- },
- "dependencies": {
- "webidl-conversions": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
- "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
- "dev": true
- }
- }
- },
- "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=="
- },
- "electron-to-chromium": {
- "version": "1.4.768",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.768.tgz",
- "integrity": "sha512-z2U3QcvNuxdkk33YV7R1bVMNq7fL23vq3WfO5BHcqrm4TnDGReouBfYKLEFh5umoK1XACjEwp8mmnhXk2EJigw==",
- "dev": true
- },
- "emittery": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz",
- "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "emojis-list": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
- "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
- "dev": true
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
- },
- "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==",
- "dev": true,
- "requires": {
- "once": "^1.4.0"
- }
- },
- "enhanced-resolve": {
- "version": "5.16.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz",
- "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "dependencies": {
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
- }
- }
- },
- "enquirer": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
- "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
- "dev": true,
- "requires": {
- "ansi-colors": "^4.1.1",
- "strip-ansi": "^6.0.1"
- }
- },
- "errno": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
- "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
- "dev": true,
- "requires": {
- "prr": "~1.0.1"
- }
- },
- "error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dev": true,
- "requires": {
- "is-arrayish": "^0.2.1"
- }
- },
- "es-abstract": {
- "version": "1.23.3",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
- "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
- "dev": true,
- "requires": {
- "array-buffer-byte-length": "^1.0.1",
- "arraybuffer.prototype.slice": "^1.0.3",
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.7",
- "data-view-buffer": "^1.0.1",
- "data-view-byte-length": "^1.0.1",
- "data-view-byte-offset": "^1.0.0",
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "es-set-tostringtag": "^2.0.3",
- "es-to-primitive": "^1.2.1",
- "function.prototype.name": "^1.1.6",
- "get-intrinsic": "^1.2.4",
- "get-symbol-description": "^1.0.2",
- "globalthis": "^1.0.3",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2",
- "has-proto": "^1.0.3",
- "has-symbols": "^1.0.3",
- "hasown": "^2.0.2",
- "internal-slot": "^1.0.7",
- "is-array-buffer": "^3.0.4",
- "is-callable": "^1.2.7",
- "is-data-view": "^1.0.1",
- "is-negative-zero": "^2.0.3",
- "is-regex": "^1.1.4",
- "is-shared-array-buffer": "^1.0.3",
- "is-string": "^1.0.7",
- "is-typed-array": "^1.1.13",
- "is-weakref": "^1.0.2",
- "object-inspect": "^1.13.1",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.5",
- "regexp.prototype.flags": "^1.5.2",
- "safe-array-concat": "^1.1.2",
- "safe-regex-test": "^1.0.3",
- "string.prototype.trim": "^1.2.9",
- "string.prototype.trimend": "^1.0.8",
- "string.prototype.trimstart": "^1.0.8",
- "typed-array-buffer": "^1.0.2",
- "typed-array-byte-length": "^1.0.1",
- "typed-array-byte-offset": "^1.0.2",
- "typed-array-length": "^1.0.6",
- "unbox-primitive": "^1.0.2",
- "which-typed-array": "^1.1.15"
- }
- },
- "es-define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
- "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
- "dev": true,
- "requires": {
- "get-intrinsic": "^1.2.4"
- }
- },
- "es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true
- },
- "es-module-lexer": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz",
- "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==",
- "dev": true
- },
- "es-object-atoms": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
- "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
- "dev": true,
- "requires": {
- "es-errors": "^1.3.0"
- }
- },
- "es-set-tostringtag": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
- "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
- "dev": true,
- "requires": {
- "get-intrinsic": "^1.2.4",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.1"
- }
- },
- "es-shim-unscopables": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
- "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
- "dev": true,
- "requires": {
- "hasown": "^2.0.0"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "escalade": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
- "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
- "dev": true
- },
- "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=="
- },
- "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
- },
- "escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "dev": true,
- "requires": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2",
- "source-map": "~0.6.1"
- },
- "dependencies": {
- "estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "optional": true
- }
- }
- },
- "eslint": {
- "version": "7.7.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz",
- "integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "ajv": "^6.10.0",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
- "debug": "^4.0.1",
- "doctrine": "^3.0.0",
- "enquirer": "^2.3.5",
- "eslint-scope": "^5.1.0",
- "eslint-utils": "^2.1.0",
- "eslint-visitor-keys": "^1.3.0",
- "espree": "^7.2.0",
- "esquery": "^1.2.0",
- "esutils": "^2.0.2",
- "file-entry-cache": "^5.0.1",
- "functional-red-black-tree": "^1.0.1",
- "glob-parent": "^5.0.0",
- "globals": "^12.1.0",
- "ignore": "^4.0.6",
- "import-fresh": "^3.0.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "js-yaml": "^3.13.1",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash": "^4.17.19",
- "minimatch": "^3.0.4",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
- "progress": "^2.0.0",
- "regexpp": "^3.1.0",
- "semver": "^7.2.1",
- "strip-ansi": "^6.0.0",
- "strip-json-comments": "^3.1.0",
- "table": "^5.2.3",
- "text-table": "^0.2.0",
- "v8-compile-cache": "^2.0.3"
- },
- "dependencies": {
- "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"
- }
- },
- "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
- }
- }
- },
- "eslint-config-prettier": {
- "version": "6.15.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz",
- "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==",
- "dev": true,
- "requires": {
- "get-stdin": "^6.0.0"
- }
- },
- "eslint-import-resolver-node": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
- "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
- "dev": true,
- "requires": {
- "debug": "^3.2.7",
- "is-core-module": "^2.13.0",
- "resolve": "^1.22.4"
- },
- "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"
- }
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- }
- }
- },
- "eslint-module-utils": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
- "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==",
- "dev": true,
- "requires": {
- "debug": "^3.2.7"
- },
- "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"
- }
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- }
- }
- },
- "eslint-plugin-import": {
- "version": "2.29.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
- "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
- "dev": true,
- "requires": {
- "array-includes": "^3.1.7",
- "array.prototype.findlastindex": "^1.2.3",
- "array.prototype.flat": "^1.3.2",
- "array.prototype.flatmap": "^1.3.2",
- "debug": "^3.2.7",
- "doctrine": "^2.1.0",
- "eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.8.0",
- "hasown": "^2.0.0",
- "is-core-module": "^2.13.1",
- "is-glob": "^4.0.3",
- "minimatch": "^3.1.2",
- "object.fromentries": "^2.0.7",
- "object.groupby": "^1.0.1",
- "object.values": "^1.1.7",
- "semver": "^6.3.1",
- "tsconfig-paths": "^3.15.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"
- }
- },
- "doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true
- }
- }
- },
- "eslint-scope": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
- "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "dev": true,
- "requires": {
- "esrecurse": "^4.3.0",
- "estraverse": "^4.1.1"
- }
- },
- "eslint-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
- "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "eslint-visitor-keys": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
- "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
- "dev": true
- },
- "espree": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
- "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
- "dev": true,
- "requires": {
- "acorn": "^7.4.0",
- "acorn-jsx": "^5.3.1",
- "eslint-visitor-keys": "^1.3.0"
- },
- "dependencies": {
- "acorn": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
- "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
- "dev": true
- }
- }
- },
- "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.5.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
- "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
- "dev": true,
- "requires": {
- "estraverse": "^5.1.0"
- },
- "dependencies": {
- "estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true
- }
- }
- },
- "esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "requires": {
- "estraverse": "^5.2.0"
- },
- "dependencies": {
- "estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true
- }
- }
- },
- "estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true
- },
- "esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true
- },
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
- },
- "events": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
- "dev": true
- },
- "exec-sh": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz",
- "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==",
- "dev": true
- },
- "execa": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
- "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
- "dev": true,
- "requires": {
- "cross-spawn": "^7.0.0",
- "get-stream": "^5.0.0",
- "human-signals": "^1.1.1",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.0",
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2",
- "strip-final-newline": "^2.0.0"
- }
- },
- "exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true
- },
- "expand-brackets": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
- "dev": true,
- "requires": {
- "debug": "^2.3.3",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "posix-character-classes": "^0.1.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "expect": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-styles": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-regex-util": "^26.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- }
- }
- },
- "express": {
- "version": "4.17.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
- "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
- "requires": {
- "accepts": "~1.3.7",
- "array-flatten": "1.1.1",
- "body-parser": "1.19.0",
- "content-disposition": "0.5.3",
- "content-type": "~1.0.4",
- "cookie": "0.4.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "~1.1.2",
- "fresh": "0.5.2",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.5",
- "qs": "6.7.0",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.1.2",
- "send": "0.17.1",
- "serve-static": "1.14.1",
- "setprototypeof": "1.1.1",
- "statuses": "~1.5.0",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
- "dependencies": {
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
- }
- }
- },
- "extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "dev": true
- },
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "dev": true,
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "dependencies": {
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
- }
- },
- "external-editor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
- "dev": true,
- "requires": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- }
- },
- "extglob": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
- "dev": true,
- "requires": {
- "array-unique": "^0.3.2",
- "define-property": "^1.0.0",
- "expand-brackets": "^2.1.4",
- "extend-shallow": "^2.0.1",
- "fragment-cache": "^0.2.1",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- }
- }
- }
- },
- "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-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
- },
- "fast-safe-stringify": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
- "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
- },
- "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"
- }
- },
- "figures": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
- "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5"
- }
- },
- "file-entry-cache": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
- "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
- "dev": true,
- "requires": {
- "flat-cache": "^2.0.1"
- }
- },
- "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"
- }
- },
- "finalhandler": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
- "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.3",
- "statuses": "~1.5.0",
- "unpipe": "~1.0.0"
- }
- },
- "find-up": {
- "version": "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": "2.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
- "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
- "dev": true,
- "requires": {
- "flatted": "^2.0.0",
- "rimraf": "2.6.3",
- "write": "1.0.3"
- },
- "dependencies": {
- "rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- }
- }
- },
- "flatted": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
- "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
- "dev": true
- },
- "follow-redirects": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
- "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
- },
- "for-each": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
- "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.3"
- }
- },
- "for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
- "dev": true
- },
- "fork-ts-checker-webpack-plugin": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.2.0.tgz",
- "integrity": "sha512-DTNbOhq6lRdjYprukX54JMeYJgQ0zMow+R5BMLwWxEX2NAXthIkwnV8DBmsWjwNLSUItKZM4TCCJbtgrtKBu2Q==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@types/json-schema": "^7.0.5",
- "chalk": "^4.1.0",
- "chokidar": "^3.4.2",
- "cosmiconfig": "^6.0.0",
- "deepmerge": "^4.2.2",
- "fs-extra": "^9.0.0",
- "memfs": "^3.1.2",
- "minimatch": "^3.0.4",
- "schema-utils": "2.7.0",
- "semver": "^7.3.2",
- "tapable": "^1.0.0"
- }
- },
- "form-data": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
- "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
- "dev": true,
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
- },
- "formidable": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
- "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
- "dev": true
- },
- "forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
- },
- "fragment-cache": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
- "dev": true,
- "requires": {
- "map-cache": "^0.2.2"
- }
- },
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
- },
- "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,
- "requires": {
- "at-least-node": "^1.0.0",
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- }
- },
- "fs-monkey": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
- "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
- "dev": true
- },
- "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.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "optional": true
- },
- "function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true
- },
- "function.prototype.name": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
- "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "es-abstract": "^1.22.1",
- "functions-have-names": "^1.2.3"
- }
- },
- "functional-red-black-tree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
- "dev": true
- },
- "functions-have-names": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
- "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-intrinsic": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
- "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
- "dev": true,
- "requires": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "has-proto": "^1.0.1",
- "has-symbols": "^1.0.3",
- "hasown": "^2.0.0"
- }
- },
- "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": "6.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
- "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
- "dev": true
- },
- "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": {
- "pump": "^3.0.0"
- }
- },
- "get-symbol-description": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
- "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.5",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.4"
- }
- },
- "get-value": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
- "dev": true
- },
- "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"
- }
- },
- "glob-to-regexp": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
- "dev": true
- },
- "globals": {
- "version": "12.4.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
- "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
- "dev": true,
- "requires": {
- "type-fest": "^0.8.1"
- },
- "dependencies": {
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
- }
- }
- },
- "globalthis": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
- "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.2.1",
- "gopd": "^1.0.1"
- }
- },
- "gopd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "dev": true,
- "requires": {
- "get-intrinsic": "^1.1.3"
- }
- },
- "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
- },
- "growly": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
- "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
- "dev": true,
- "optional": true
- },
- "has-bigints": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
- "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
- "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=="
- },
- "has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "dev": true,
- "requires": {
- "es-define-property": "^1.0.0"
- }
- },
- "has-proto": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
- "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
- "dev": true
- },
- "has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
- "dev": true
- },
- "has-tostringtag": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.3"
- }
- },
- "has-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
- "dev": true,
- "requires": {
- "get-value": "^2.0.6",
- "has-values": "^1.0.0",
- "isobject": "^3.0.0"
- }
- },
- "has-values": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
- "dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
- },
- "dependencies": {
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.2"
- }
- },
- "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-encoding-sniffer": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
- "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
- "dev": true,
- "requires": {
- "whatwg-encoding": "^1.0.5"
- }
- },
- "html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
- },
- "http-errors": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
- "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
- "requires": {
- "depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.1",
- "statuses": ">= 1.5.0 < 2",
- "toidentifier": "1.0.0"
- }
- },
- "http-proxy-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
- "dev": true,
- "requires": {
- "@tootallnate/once": "1",
- "agent-base": "6",
- "debug": "4"
- },
- "dependencies": {
- "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"
- }
- },
- "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
- }
- }
- },
- "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==",
- "dev": true,
- "requires": {
- "agent-base": "6",
- "debug": "4"
- },
- "dependencies": {
- "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"
- }
- },
- "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
- }
- }
- },
- "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
- },
- "iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "dev": true
- },
- "ignore": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
- "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
- "dev": true
- },
- "import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "dev": true,
- "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
- },
- "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.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
- },
- "inquirer": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
- "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.1.0",
- "cli-cursor": "^3.1.0",
- "cli-width": "^3.0.0",
- "external-editor": "^3.0.3",
- "figures": "^3.0.0",
- "lodash": "^4.17.19",
- "mute-stream": "0.0.8",
- "run-async": "^2.4.0",
- "rxjs": "^6.6.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0",
- "through": "^2.3.6"
- }
- },
- "internal-slot": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
- "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
- "dev": true,
- "requires": {
- "es-errors": "^1.3.0",
- "hasown": "^2.0.0",
- "side-channel": "^1.0.4"
- }
- },
- "interpret": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
- "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
- "dev": true
- },
- "ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
- },
- "is-accessor-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
- "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
- "dev": true,
- "requires": {
- "hasown": "^2.0.0"
- }
- },
- "is-array-buffer": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
- "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.2.1"
- }
- },
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
- },
- "is-bigint": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
- "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
- "dev": true,
- "requires": {
- "has-bigints": "^1.0.1"
- }
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-boolean-object": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
- "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "dev": true
- },
- "is-callable": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
- "dev": true
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "dev": true,
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "is-core-module": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
- "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
- "dev": true,
- "requires": {
- "hasown": "^2.0.0"
- }
- },
- "is-data-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz",
- "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
- "dev": true,
- "requires": {
- "hasown": "^2.0.0"
- }
- },
- "is-data-view": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
- "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
- "dev": true,
- "requires": {
- "is-typed-array": "^1.1.13"
- }
- },
- "is-date-object": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
- "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
- "dev": true,
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- }
- },
- "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,
- "optional": true
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
- "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==",
- "dev": true
- },
- "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-interactive": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
- "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
- "dev": true
- },
- "is-negative-zero": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
- "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
- "dev": true
- },
- "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-number-object": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
- "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
- "dev": true,
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "dev": true,
- "requires": {
- "isobject": "^3.0.1"
- }
- },
- "is-potential-custom-element-name": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
- "dev": true
- },
- "is-regex": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
- "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-shared-array-buffer": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
- "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7"
- }
- },
- "is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true
- },
- "is-string": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
- "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
- "dev": true,
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-symbol": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
- "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.2"
- }
- },
- "is-typed-array": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
- "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
- "dev": true,
- "requires": {
- "which-typed-array": "^1.1.14"
- }
- },
- "is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
- "dev": true
- },
- "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
- },
- "is-weakref": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
- "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2"
- }
- },
- "is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
- "dev": true
- },
- "is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
- "optional": true,
- "requires": {
- "is-docker": "^2.0.0"
- }
- },
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
- "dev": true
- },
- "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
- },
- "istanbul-lib-instrument": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
- "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.7.5",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.0.0",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true
- }
- }
- },
- "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,
- "requires": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.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"
- },
- "dependencies": {
- "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"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "istanbul-reports": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
- "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
- "dev": true,
- "requires": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- }
- },
- "iterare": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz",
- "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q=="
- },
- "jest": {
- "version": "26.4.2",
- "resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz",
- "integrity": "sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw==",
- "dev": true,
- "requires": {
- "@jest/core": "^26.4.2",
- "import-local": "^3.0.2",
- "jest-cli": "^26.4.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-cli": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz",
- "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==",
- "dev": true,
- "requires": {
- "@jest/core": "^26.6.3",
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.4",
- "import-local": "^3.0.2",
- "is-ci": "^2.0.0",
- "jest-config": "^26.6.3",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "prompts": "^2.0.1",
- "yargs": "^15.4.1"
- }
- }
- }
- },
- "jest-changed-files": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz",
- "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "execa": "^4.0.0",
- "throat": "^5.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "jest-config": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz",
- "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.1.0",
- "@jest/test-sequencer": "^26.6.3",
- "@jest/types": "^26.6.2",
- "babel-jest": "^26.6.3",
- "chalk": "^4.0.0",
- "deepmerge": "^4.2.2",
- "glob": "^7.1.1",
- "graceful-fs": "^4.2.4",
- "jest-environment-jsdom": "^26.6.2",
- "jest-environment-node": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "jest-jasmine2": "^26.6.3",
- "jest-regex-util": "^26.0.0",
- "jest-resolve": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-diff": {
- "version": "25.5.0",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz",
- "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==",
- "dev": true,
- "requires": {
- "chalk": "^3.0.0",
- "diff-sequences": "^25.2.6",
- "jest-get-type": "^25.2.6",
- "pretty-format": "^25.5.0"
- },
- "dependencies": {
- "chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- }
- }
- },
- "jest-docblock": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz",
- "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==",
- "dev": true,
- "requires": {
- "detect-newline": "^3.0.0"
- }
- },
- "jest-each": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz",
- "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "jest-util": "^26.6.2",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-environment-jsdom": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz",
- "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==",
- "dev": true,
- "requires": {
- "@jest/environment": "^26.6.2",
- "@jest/fake-timers": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "jest-mock": "^26.6.2",
- "jest-util": "^26.6.2",
- "jsdom": "^16.4.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "jest-environment-node": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz",
- "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==",
- "dev": true,
- "requires": {
- "@jest/environment": "^26.6.2",
- "@jest/fake-timers": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "jest-mock": "^26.6.2",
- "jest-util": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "jest-get-type": {
- "version": "25.2.6",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz",
- "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==",
- "dev": true
- },
- "jest-haste-map": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
- "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/graceful-fs": "^4.1.2",
- "@types/node": "*",
- "anymatch": "^3.0.3",
- "fb-watchman": "^2.0.0",
- "fsevents": "^2.1.2",
- "graceful-fs": "^4.2.4",
- "jest-regex-util": "^26.0.0",
- "jest-serializer": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-worker": "^26.6.2",
- "micromatch": "^4.0.2",
- "sane": "^4.0.3",
- "walker": "^1.0.7"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-worker": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
- "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^7.0.0"
- }
- }
- }
- },
- "jest-jasmine2": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz",
- "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==",
- "dev": true,
- "requires": {
- "@babel/traverse": "^7.1.0",
- "@jest/environment": "^26.6.2",
- "@jest/source-map": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "co": "^4.6.0",
- "expect": "^26.6.2",
- "is-generator-fn": "^2.0.0",
- "jest-each": "^26.6.2",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-runtime": "^26.6.3",
- "jest-snapshot": "^26.6.2",
- "jest-util": "^26.6.2",
- "pretty-format": "^26.6.2",
- "throat": "^5.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-leak-detector": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz",
- "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==",
- "dev": true,
- "requires": {
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-matcher-utils": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "diff-sequences": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
- "dev": true
- },
- "jest-diff": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-mock": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz",
- "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/node": "*"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "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
- },
- "jest-regex-util": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz",
- "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==",
- "dev": true
- },
- "jest-resolve": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz",
- "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "jest-pnp-resolver": "^1.2.2",
- "jest-util": "^26.6.2",
- "read-pkg-up": "^7.0.1",
- "resolve": "^1.18.1",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "jest-resolve-dependencies": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz",
- "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-snapshot": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "jest-runner": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz",
- "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==",
- "dev": true,
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/environment": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "emittery": "^0.7.1",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.4",
- "jest-config": "^26.6.3",
- "jest-docblock": "^26.0.0",
- "jest-haste-map": "^26.6.2",
- "jest-leak-detector": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-resolve": "^26.6.2",
- "jest-runtime": "^26.6.3",
- "jest-util": "^26.6.2",
- "jest-worker": "^26.6.2",
- "source-map-support": "^0.5.6",
- "throat": "^5.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "jest-worker": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
- "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^7.0.0"
- }
- }
- }
- },
- "jest-runtime": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz",
- "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==",
- "dev": true,
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/environment": "^26.6.2",
- "@jest/fake-timers": "^26.6.2",
- "@jest/globals": "^26.6.2",
- "@jest/source-map": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0",
- "cjs-module-lexer": "^0.6.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.4",
- "jest-config": "^26.6.3",
- "jest-haste-map": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-mock": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-resolve": "^26.6.2",
- "jest-snapshot": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "slash": "^3.0.0",
- "strip-bom": "^4.0.0",
- "yargs": "^15.4.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "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
- }
- }
- },
- "jest-serializer": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz",
- "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "graceful-fs": "^4.2.4"
- }
- },
- "jest-snapshot": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz",
- "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/babel__traverse": "^7.0.4",
- "@types/prettier": "^2.0.0",
- "chalk": "^4.0.0",
- "expect": "^26.6.2",
- "graceful-fs": "^4.2.4",
- "jest-diff": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "jest-haste-map": "^26.6.2",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-resolve": "^26.6.2",
- "natural-compare": "^1.4.0",
- "pretty-format": "^26.6.2",
- "semver": "^7.3.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "diff-sequences": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
- "dev": true
- },
- "jest-diff": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
- "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "is-ci": "^2.0.0",
- "micromatch": "^4.0.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "jest-validate": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz",
- "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "camelcase": "^6.0.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "leven": "^3.1.0",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "dev": true,
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-watcher": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz",
- "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==",
- "dev": true,
- "requires": {
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "jest-util": "^26.6.2",
- "string-length": "^4.0.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@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,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- }
- }
- },
- "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": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "dependencies": {
- "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-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "js-yaml": {
- "version": "3.14.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"
- }
- },
- "jsdom": {
- "version": "16.7.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
- "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
- "dev": true,
- "requires": {
- "abab": "^2.0.5",
- "acorn": "^8.2.4",
- "acorn-globals": "^6.0.0",
- "cssom": "^0.4.4",
- "cssstyle": "^2.3.0",
- "data-urls": "^2.0.0",
- "decimal.js": "^10.2.1",
- "domexception": "^2.0.1",
- "escodegen": "^2.0.0",
- "form-data": "^3.0.0",
- "html-encoding-sniffer": "^2.0.1",
- "http-proxy-agent": "^4.0.1",
- "https-proxy-agent": "^5.0.0",
- "is-potential-custom-element-name": "^1.0.1",
- "nwsapi": "^2.2.0",
- "parse5": "6.0.1",
- "saxes": "^5.0.1",
- "symbol-tree": "^3.2.4",
- "tough-cookie": "^4.0.0",
- "w3c-hr-time": "^1.0.2",
- "w3c-xmlserializer": "^2.0.0",
- "webidl-conversions": "^6.1.0",
- "whatwg-encoding": "^1.0.5",
- "whatwg-mimetype": "^2.3.0",
- "whatwg-url": "^8.5.0",
- "ws": "^7.4.6",
- "xml-name-validator": "^3.0.0"
- },
- "dependencies": {
- "tr46": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
- "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
- "dev": true,
- "requires": {
- "punycode": "^2.1.1"
- }
- },
- "webidl-conversions": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
- "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
- "dev": true
- },
- "whatwg-url": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
- "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
- "dev": true,
- "requires": {
- "lodash": "^4.7.0",
- "tr46": "^2.1.0",
- "webidl-conversions": "^6.1.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-better-errors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
- "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
- "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==",
- "dev": true
- },
- "json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
- },
- "json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true
- },
- "json5": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "jsonc-parser": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
- "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
- "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"
- }
- },
- "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
- },
- "levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "requires": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- }
- },
- "lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true
- },
- "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
- },
- "loader-utils": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
- "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.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
- },
- "lodash.toarray": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
- "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==",
- "dev": true
- },
- "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,
- "requires": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- }
- },
- "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"
- }
- },
- "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
- },
- "magic-string": {
- "version": "0.25.7",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
- "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
- "dev": true,
- "requires": {
- "sourcemap-codec": "^1.4.4"
- }
- },
- "make-dir": {
- "version": "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": {
- "semver": "^7.5.3"
- }
- },
- "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-cache": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
- "dev": true
- },
- "map-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
- "dev": true,
- "requires": {
- "object-visit": "^1.0.0"
- }
- },
- "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=="
- },
- "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": {
- "fs-monkey": "^1.0.4"
- }
- },
- "memory-fs": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
- "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
- "dev": true,
- "requires": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true
- },
- "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,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
- }
- },
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
- },
- "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==",
- "dev": true
- },
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
- },
- "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"
- }
- },
- "mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
- },
- "mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
- },
- "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==",
- "requires": {
- "mime-db": "1.52.0"
- }
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
- "minimatch": {
- "version": "3.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"
- }
- },
- "minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
- },
- "mixin-deep": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
- "dev": true,
- "requires": {
- "for-in": "^1.0.2",
- "is-extendable": "^1.0.1"
- },
- "dependencies": {
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
- }
- },
- "mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "requires": {
- "minimist": "^1.2.6"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "multer": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz",
- "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==",
- "requires": {
- "append-field": "^1.0.0",
- "busboy": "^0.2.11",
- "concat-stream": "^1.5.2",
- "mkdirp": "^0.5.1",
- "object-assign": "^4.1.1",
- "on-finished": "^2.3.0",
- "type-is": "^1.6.4",
- "xtend": "^4.0.0"
- }
- },
- "mute-stream": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
- "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
- "dev": true
- },
- "nanomatch": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
- "dev": true,
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "fragment-cache": "^0.2.1",
- "is-windows": "^1.0.2",
- "kind-of": "^6.0.2",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- }
- },
- "natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
- },
- "negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
- },
- "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
- },
- "nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "dev": true
- },
- "node-emoji": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
- "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
- "dev": true,
- "requires": {
- "lodash.toarray": "^4.4.0"
- }
- },
- "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==",
- "requires": {
- "whatwg-url": "^5.0.0"
- }
- },
- "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-notifier": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz",
- "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==",
- "dev": true,
- "optional": true,
- "requires": {
- "growly": "^1.3.0",
- "is-wsl": "^2.2.0",
- "semver": "^7.3.2",
- "shellwords": "^0.1.1",
- "uuid": "^8.3.0",
- "which": "^2.0.2"
- }
- },
- "node-releases": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
- "dev": true
- },
- "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.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "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-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "requires": {
- "path-key": "^3.0.0"
- }
- },
- "nwsapi": {
- "version": "2.2.10",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz",
- "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
- "dev": true
- },
- "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-copy": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
- "dev": true,
- "requires": {
- "copy-descriptor": "^0.1.0",
- "define-property": "^0.2.5",
- "kind-of": "^3.0.3"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "object-hash": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz",
- "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ=="
- },
- "object-inspect": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
- "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
- "dev": true
- },
- "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
- },
- "object-visit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
- "dev": true,
- "requires": {
- "isobject": "^3.0.0"
- }
- },
- "object.assign": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
- "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.5",
- "define-properties": "^1.2.1",
- "has-symbols": "^1.0.3",
- "object-keys": "^1.1.1"
- }
- },
- "object.fromentries": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
- "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0"
- }
- },
- "object.groupby": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
- "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2"
- }
- },
- "object.pick": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
- "dev": true,
- "requires": {
- "isobject": "^3.0.1"
- }
- },
- "object.values": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
- "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- }
- },
- "on-finished": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
- "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
- "requires": {
- "ee-first": "1.1.1"
- }
- },
- "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==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "optional": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz",
- "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==",
- "dev": true
- },
- "optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "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.5"
- }
- },
- "ora": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz",
- "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==",
- "dev": true,
- "requires": {
- "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"
- }
- },
- "os-name": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.0.tgz",
- "integrity": "sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg==",
- "dev": true,
- "requires": {
- "macos-release": "^2.2.0",
- "windows-release": "^4.0.0"
- }
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
- "dev": true
- },
- "p-each-series": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
- "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
- "dev": true
- },
- "p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
- "dev": true
- },
- "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-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==",
- "dev": true,
- "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==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- }
- },
- "parse5": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
- "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
- "dev": true
- },
- "parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
- },
- "pascalcase": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
- "dev": true
- },
- "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==",
- "dev": true
- },
- "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-to-regexp": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
- "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
- },
- "picocolors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
- "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
- "dev": true
- },
- "picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true
- },
- "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
- },
- "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==",
- "dev": true
- },
- "posix-character-classes": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
- "dev": true
- },
- "possible-typed-array-names": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
- "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
- "dev": true
- },
- "prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true
- },
- "prettier": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
- "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
- "dev": true
- },
- "pretty-format": {
- "version": "25.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz",
- "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^25.5.0",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^16.12.0"
- }
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
- },
- "progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "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"
- }
- },
- "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==",
- "requires": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- }
- },
- "prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
- "dev": true
- },
- "psl": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
- "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
- "dev": true
- },
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true
- },
- "qs": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
- "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
- },
- "querystringify": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
- "dev": true
- },
- "randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
- "requires": {
- "safe-buffer": "^5.1.0"
- }
- },
- "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=="
- },
- "raw-body": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
- "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
- "requires": {
- "bytes": "3.1.0",
- "http-errors": "1.7.2",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- }
- },
- "react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true
- },
- "read-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"
- },
- "dependencies": {
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
- }
- }
- },
- "readable-stream": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
- "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.1",
- "isarray": "0.0.1",
- "string_decoder": "~0.10.x"
- }
- },
- "readdirp": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
- "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
- "dev": true,
- "requires": {
- "picomatch": "^2.2.1"
- }
- },
- "rechoir": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
- "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
- "dev": true,
- "requires": {
- "resolve": "^1.1.6"
- }
- },
- "reflect-metadata": {
- "version": "0.1.14",
- "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
- "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A=="
- },
- "regex-not": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
- "dev": true,
- "requires": {
- "extend-shallow": "^3.0.2",
- "safe-regex": "^1.1.0"
- }
- },
- "regexp.prototype.flags": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
- "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.6",
- "define-properties": "^1.2.1",
- "es-errors": "^1.3.0",
- "set-function-name": "^2.0.1"
- }
- },
- "regexpp": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
- "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
- "dev": true
- },
- "remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
- "dev": true
- },
- "repeat-element": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
- "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
- "dev": true
- },
- "repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
- "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-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
- "requires-port": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
- "dev": true
- },
- "resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "dev": true,
- "requires": {
- "is-core-module": "^2.13.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==",
- "dev": true
- },
- "resolve-url": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
- "dev": true
- },
- "restore-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
- "dev": true,
- "requires": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
- }
- },
- "ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
- "dev": true
- },
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "rsvp": {
- "version": "4.8.5",
- "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
- "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==",
- "dev": true
- },
- "run-async": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
- "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
- "dev": true
- },
- "rxjs": {
- "version": "6.6.7",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
- "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
- "requires": {
- "tslib": "^1.9.0"
- },
- "dependencies": {
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
- }
- }
- },
- "safe-array-concat": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
- "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "get-intrinsic": "^1.2.4",
- "has-symbols": "^1.0.3",
- "isarray": "^2.0.5"
- },
- "dependencies": {
- "isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
- "dev": true
- }
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
- "safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
- "dev": true,
- "requires": {
- "ret": "~0.1.10"
- }
- },
- "safe-regex-test": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
- "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.6",
- "es-errors": "^1.3.0",
- "is-regex": "^1.1.4"
- }
- },
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "sane": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz",
- "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==",
- "dev": true,
- "requires": {
- "@cnakazawa/watch": "^1.0.3",
- "anymatch": "^2.0.0",
- "capture-exit": "^2.0.0",
- "exec-sh": "^0.3.2",
- "execa": "^1.0.0",
- "fb-watchman": "^2.0.0",
- "micromatch": "^3.1.4",
- "minimist": "^1.1.1",
- "walker": "~1.0.5"
- },
- "dependencies": {
- "anymatch": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
- "dev": true,
- "requires": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
- }
- },
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "dev": true,
- "requires": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- }
- },
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
- "dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
- "dev": true
- },
- "micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "dev": true,
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- }
- },
- "normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
- "dev": true,
- "requires": {
- "remove-trailing-separator": "^1.0.1"
- }
- },
- "npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
- "dev": true,
- "requires": {
- "path-key": "^2.0.0"
- }
- },
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
- "dev": true
- },
- "semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "dev": true
- },
- "shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
- "dev": true,
- "requires": {
- "shebang-regex": "^1.0.0"
- }
- },
- "shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
- "dev": true
- },
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
- "dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- }
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
- }
- },
- "saxes": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
- "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
- "dev": true,
- "requires": {
- "xmlchars": "^2.2.0"
- }
- },
- "schema-utils": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
- "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
- "dev": true,
- "requires": {
- "@types/json-schema": "^7.0.4",
- "ajv": "^6.12.2",
- "ajv-keywords": "^3.4.1"
- }
- },
- "semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
- "dev": true
- },
- "send": {
- "version": "0.17.1",
- "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
- "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
- "requires": {
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "destroy": "~1.0.4",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "~1.7.2",
- "mime": "1.6.0",
- "ms": "2.1.1",
- "on-finished": "~2.3.0",
- "range-parser": "~1.2.1",
- "statuses": "~1.5.0"
- },
- "dependencies": {
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
- }
- }
- },
- "serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dev": true,
- "requires": {
- "randombytes": "^2.1.0"
- }
- },
- "serve-static": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
- "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.17.1"
- }
- },
- "set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dev": true
- },
- "set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
- "dev": true,
- "requires": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
- }
- },
- "set-function-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
- "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
- "dev": true,
- "requires": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.2"
- }
- },
- "set-value": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
- "dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.3",
- "split-string": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "setprototypeof": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
- "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "shelljs": {
- "version": "0.8.4",
- "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
- "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
- "dev": true,
- "requires": {
- "glob": "^7.0.0",
- "interpret": "^1.0.0",
- "rechoir": "^0.6.2"
- }
- },
- "shellwords": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
- "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
- "dev": true,
- "optional": true
- },
- "side-channel": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
- "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.4",
- "object-inspect": "^1.13.1"
- }
- },
- "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==",
- "dev": true
- },
- "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": "2.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
- "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "astral-regex": "^1.0.0",
- "is-fullwidth-code-point": "^2.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "dev": true
- }
- }
- },
- "snapdragon": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
- "dev": true,
- "requires": {
- "base": "^0.11.1",
- "debug": "^2.2.0",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "map-cache": "^0.2.2",
- "source-map": "^0.5.6",
- "source-map-resolve": "^0.5.0",
- "use": "^3.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "dev": true
- }
- }
- },
- "snapdragon-node": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
- "dev": true,
- "requires": {
- "define-property": "^1.0.0",
- "isobject": "^3.0.0",
- "snapdragon-util": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- }
- }
- }
- },
- "snapdragon-util": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
- "dev": true,
- "requires": {
- "kind-of": "^3.2.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "source-list-map": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
- "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
- "dev": true
- },
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
- "dev": true
- },
- "source-map-resolve": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
- "dev": true,
- "requires": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0",
- "resolve-url": "^0.2.1",
- "source-map-url": "^0.4.0",
- "urix": "^0.1.0"
- }
- },
- "source-map-support": {
- "version": "0.5.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": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "source-map-url": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
- "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
- "dev": true
- },
- "sourcemap-codec": {
- "version": "1.4.8",
- "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
- "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "dev": true
- },
- "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,
- "requires": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "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.17",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
- "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
- "dev": true
- },
- "split-string": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
- "dev": true,
- "requires": {
- "extend-shallow": "^3.0.0"
- }
- },
- "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
- }
- }
- },
- "static-extend": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
- "dev": true,
- "requires": {
- "define-property": "^0.2.5",
- "object-copy": "^0.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- }
- }
- },
- "statuses": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
- },
- "streamsearch": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
- "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA=="
- },
- "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==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
- "string.prototype.trim": {
- "version": "1.2.9",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
- "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.0",
- "es-object-atoms": "^1.0.0"
- }
- },
- "string.prototype.trimend": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
- "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- }
- },
- "string.prototype.trimstart": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
- "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- }
- },
- "string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
- },
- "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,
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
- "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
- },
- "strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
- "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==",
- "dev": true
- },
- "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
- },
- "superagent": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
- "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==",
- "dev": true,
- "requires": {
- "component-emitter": "^1.2.0",
- "cookiejar": "^2.1.0",
- "debug": "^3.1.0",
- "extend": "^3.0.0",
- "form-data": "^2.3.1",
- "formidable": "^1.2.0",
- "methods": "^1.1.1",
- "mime": "^1.4.1",
- "qs": "^6.5.1",
- "readable-stream": "^2.3.5"
- },
- "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"
- }
- },
- "form-data": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
- "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
- "dev": true,
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
- "mime-types": "^2.1.12"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "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,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
- }
- },
- "supertest": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz",
- "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==",
- "dev": true,
- "requires": {
- "methods": "^1.1.2",
- "superagent": "^3.8.3"
- }
- },
- "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"
- }
- },
- "supports-hyperlinks": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
- "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0",
- "supports-color": "^7.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
- },
- "symbol-observable": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-3.0.0.tgz",
- "integrity": "sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q==",
- "dev": true
- },
- "symbol-tree": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
- "dev": true
- },
- "table": {
- "version": "5.4.6",
- "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
- "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
- "dev": true,
- "requires": {
- "ajv": "^6.10.2",
- "lodash": "^4.17.14",
- "slice-ansi": "^2.1.0",
- "string-width": "^3.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
- "dev": true
- },
- "emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
- }
- },
- "tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
- "dev": true
- },
- "terminal-link": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
- "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^4.2.1",
- "supports-hyperlinks": "^2.0.0"
- }
- },
- "terser": {
- "version": "5.31.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz",
- "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==",
- "dev": true,
- "requires": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- }
- }
- },
- "terser-webpack-plugin": {
- "version": "5.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": {
- "@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"
- },
- "dependencies": {
- "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": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- }
- }
- }
- },
- "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-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
- },
- "throat": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
- "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
- "dev": true
- },
- "through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
- "dev": true
- },
- "tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
- "requires": {
- "os-tmpdir": "~1.0.2"
- }
- },
- "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-object-path": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "to-regex": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
- "dev": true,
- "requires": {
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "regex-not": "^1.0.2",
- "safe-regex": "^1.1.0"
- }
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "toidentifier": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
- "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
- },
- "tough-cookie": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
- "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
- "dev": true,
- "requires": {
- "psl": "^1.1.33",
- "punycode": "^2.1.1",
- "universalify": "^0.2.0",
- "url-parse": "^1.5.3"
- },
- "dependencies": {
- "universalify": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
- "dev": true
- }
- }
- },
- "tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
- },
- "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
- },
- "ts-jest": {
- "version": "26.2.0",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.2.0.tgz",
- "integrity": "sha512-9+y2qwzXdAImgLSYLXAb/Rhq9+K4rbt0417b8ai987V60g2uoNWBBmMkYgutI7D8Zhu+IbCSHbBtrHxB9d7xyA==",
- "dev": true,
- "requires": {
- "@types/jest": "26.x",
- "bs-logger": "0.x",
- "buffer-from": "1.x",
- "fast-json-stable-stringify": "2.x",
- "jest-util": "26.x",
- "json5": "2.x",
- "lodash.memoize": "4.x",
- "make-error": "1.x",
- "mkdirp": "1.x",
- "semver": "7.x",
- "yargs-parser": "18.x"
- },
- "dependencies": {
- "json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true
- },
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true
- }
- }
- },
- "ts-loader": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.2.tgz",
- "integrity": "sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ==",
- "dev": true,
- "requires": {
- "chalk": "^2.3.0",
- "enhanced-resolve": "^4.0.0",
- "loader-utils": "^1.0.2",
- "micromatch": "^4.0.0",
- "semver": "^6.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "enhanced-resolve": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
- "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "memory-fs": "^0.5.0",
- "tapable": "^1.0.0"
- }
- },
- "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
- },
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "ts-node": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
- "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
- "dev": true,
- "requires": {
- "arg": "^4.1.0",
- "diff": "^4.0.1",
- "make-error": "^1.1.1",
- "source-map-support": "^0.5.17",
- "yn": "3.1.1"
- }
- },
- "tsconfig-paths": {
- "version": "3.15.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
- "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
- "dev": true,
- "requires": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.2",
- "minimist": "^1.2.6",
- "strip-bom": "^3.0.0"
- }
- },
- "tsconfig-paths-webpack-plugin": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.1.tgz",
- "integrity": "sha512-n5CMlUUj+N5pjBhBACLq4jdr9cPTitySCjIosoQm0zwK99gmrcTGAfY9CwxRFT9+9OleNWXPRUcxsKP4AYExxQ==",
- "dev": true,
- "requires": {
- "chalk": "^4.1.0",
- "enhanced-resolve": "^5.7.0",
- "tsconfig-paths": "^3.9.0"
- }
- },
- "tslib": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
- "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
- },
- "tsutils": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
- "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
- "dev": true,
- "requires": {
- "tslib": "^1.8.1"
- },
- "dependencies": {
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
- }
- }
- },
- "type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "requires": {
- "prelude-ls": "^1.2.1"
- }
- },
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
- },
- "type-fest": {
- "version": "0.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
- },
- "type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "requires": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- }
- },
- "typed-array-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
- "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "es-errors": "^1.3.0",
- "is-typed-array": "^1.1.13"
- }
- },
- "typed-array-byte-length": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
- "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-proto": "^1.0.3",
- "is-typed-array": "^1.1.13"
- }
- },
- "typed-array-byte-offset": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
- "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
- "dev": true,
- "requires": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-proto": "^1.0.3",
- "is-typed-array": "^1.1.13"
- }
- },
- "typed-array-length": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
- "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-proto": "^1.0.3",
- "is-typed-array": "^1.1.13",
- "possible-typed-array-names": "^1.0.0"
- }
- },
- "typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
- },
- "typedarray-to-buffer": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
- "dev": true,
- "requires": {
- "is-typedarray": "^1.0.0"
- }
- },
- "typescript": {
- "version": "3.9.10",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
- "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
- "dev": true
- },
- "unbox-primitive": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
- "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "has-bigints": "^1.0.2",
- "has-symbols": "^1.0.3",
- "which-boxed-primitive": "^1.0.2"
- }
- },
- "union-value": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
- "dev": true,
- "requires": {
- "arr-union": "^3.1.0",
- "get-value": "^2.0.6",
- "is-extendable": "^0.1.1",
- "set-value": "^2.0.1"
- }
- },
- "universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true
- },
- "unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
- },
- "unset-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
- "dev": true,
- "requires": {
- "has-value": "^0.3.1",
- "isobject": "^3.0.0"
- },
- "dependencies": {
- "has-value": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
- "dev": true,
- "requires": {
- "get-value": "^2.0.3",
- "has-values": "^0.1.4",
- "isobject": "^2.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
- "dev": true,
- "requires": {
- "isarray": "1.0.0"
- }
- }
- }
- },
- "has-values": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
- "dev": true
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true
- }
- }
- },
- "update-browserslist-db": {
- "version": "1.0.16",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
- "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
- "dev": true,
- "requires": {
- "escalade": "^3.1.2",
- "picocolors": "^1.0.1"
- }
- },
- "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"
- }
- },
- "urix": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
- "dev": true
- },
- "url-parse": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "dev": true,
- "requires": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
- }
- },
- "use": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
- "dev": true
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
- },
- "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=="
- },
- "uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
- },
- "v8-compile-cache": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
- "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==",
- "dev": true
- },
- "v8-to-istanbul": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
- "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^1.6.0",
- "source-map": "^0.7.3"
- }
- },
- "validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "dev": true,
- "requires": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
- },
- "w3c-hr-time": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
- "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
- "dev": true,
- "requires": {
- "browser-process-hrtime": "^1.0.0"
- }
- },
- "w3c-xmlserializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
- "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
- "dev": true,
- "requires": {
- "xml-name-validator": "^3.0.0"
- }
- },
- "walker": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
- "dev": true,
- "requires": {
- "makeerror": "1.0.12"
- }
- },
- "watchpack": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
- "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==",
- "dev": true,
- "requires": {
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.1.2"
- }
- },
- "wcwidth": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
- "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
- "dev": true,
- "requires": {
- "defaults": "^1.0.3"
- }
- },
- "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=="
- },
- "webpack": {
- "version": "5.28.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz",
- "integrity": "sha512-1xllYVmA4dIvRjHzwELgW4KjIU1fW4PEuEnjsylz7k7H5HgPOctIq7W1jrt3sKH9yG5d72//XWzsHhfoWvsQVg==",
- "dev": true,
- "requires": {
- "@types/eslint-scope": "^3.7.0",
- "@types/estree": "^0.0.46",
- "@webassemblyjs/ast": "1.11.0",
- "@webassemblyjs/wasm-edit": "1.11.0",
- "@webassemblyjs/wasm-parser": "1.11.0",
- "acorn": "^8.0.4",
- "browserslist": "^4.14.5",
- "chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.7.0",
- "es-module-lexer": "^0.4.0",
- "eslint-scope": "^5.1.1",
- "events": "^3.2.0",
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.2.4",
- "json-parse-better-errors": "^1.0.2",
- "loader-runner": "^4.2.0",
- "mime-types": "^2.1.27",
- "neo-async": "^2.6.2",
- "schema-utils": "^3.0.0",
- "tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.1.1",
- "watchpack": "^2.0.0",
- "webpack-sources": "^2.1.1"
- },
- "dependencies": {
- "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": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- }
- },
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
- }
- }
- },
- "webpack-node-externals": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-2.5.2.tgz",
- "integrity": "sha512-aHdl/y2N7PW2Sx7K+r3AxpJO+aDMcYzMQd60Qxefq3+EwhewSbTBqNumOsCE1JsCUNoyfGj5465N0sSf6hc/5w==",
- "dev": true
- },
- "webpack-sources": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz",
- "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==",
- "dev": true,
- "requires": {
- "source-list-map": "^2.0.1",
- "source-map": "^0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "whatwg-encoding": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
- "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
- "dev": true,
- "requires": {
- "iconv-lite": "0.4.24"
- }
- },
- "whatwg-mimetype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
- "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
- "dev": true
- },
- "whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "requires": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "which-boxed-primitive": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
- "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
- "dev": true,
- "requires": {
- "is-bigint": "^1.0.1",
- "is-boolean-object": "^1.1.0",
- "is-number-object": "^1.0.4",
- "is-string": "^1.0.5",
- "is-symbol": "^1.0.3"
- }
- },
- "which-module": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
- "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
- "dev": true
- },
- "which-typed-array": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
- "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
- "dev": true,
- "requires": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-tostringtag": "^1.0.2"
- }
- },
- "windows-release": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
- "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==",
- "dev": true,
- "requires": {
- "execa": "^4.0.2"
- }
- },
- "word-wrap": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "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"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
- },
- "write": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
- "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
- "dev": true,
- "requires": {
- "mkdirp": "^0.5.1"
- }
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
- "dev": true
- },
- "xml-name-validator": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
- "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
- "dev": true
- },
- "xmlchars": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
- "dev": true
- },
- "xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
- },
- "y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "dev": true
- },
- "yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- },
- "yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "dev": true
- },
- "yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "dev": true,
- "requires": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- }
- },
- "yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- },
- "yn": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
- "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "dev": true
- }
- }
-}
diff --git a/package.json b/package.json
index c97a10d..b410504 100644
--- a/package.json
+++ b/package.json
@@ -1,54 +1,122 @@
{
"name": "nestjs-template",
- "version": "0.0.1",
+ "version": "1.1.0",
"description": "",
"author": "",
"private": true,
- "license": "UNLICENSED",
+ "license": "MIT",
"scripts": {
+ "typeorm": "env-cmd ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
+ "migration:generate": "npm run typeorm -- --dataSource=src/database/data-source.ts migration:generate",
+ "migration:create": "npm run typeorm -- migration:create",
+ "migration:run": "npm run typeorm -- --dataSource=src/database/data-source.ts migration:run",
+ "migration:revert": "npm run typeorm -- --dataSource=src/database/data-source.ts migration:revert",
+ "schema:drop": "npm run typeorm -- --dataSource=src/database/data-source.ts schema:drop",
+ "seed:create:relational": "hygen seeds create-relational",
+ "seed:create:document": "hygen seeds create-document",
+ "app:config": "ts-node -r tsconfig-paths/register ./.install-scripts/index.ts && npm install && npm run lint -- --fix",
+ "seed:run:relational": "ts-node -r tsconfig-paths/register ./src/database/seeds/relational/run-seed.ts",
+ "seed:run:document": "ts-node -r tsconfig-paths/register ./src/database/seeds/document/run-seed.ts",
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
+ "start:swc": "nest start -b swc -w",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
- "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
- "test:e2e": "jest --config ./test/jest-e2e.json"
+ "test:e2e": "env-cmd jest --config ./test/jest-e2e.json",
+ "test:e2e:document:docker": "docker compose -f docker-compose.document.test.yaml --env-file env-example-document -p tests up -d --build && docker compose -f docker-compose.document.test.yaml -p tests exec api /opt/wait-for-it.sh -t 0 localhost:3000 -- npm run test:e2e -- --watchAll --runInBand && docker compose -f docker-compose.document.test.yaml -p tests down && docker compose -p tests rm -svf",
+ "test:e2e:relational:docker": "docker compose -f docker-compose.relational.test.yaml --env-file env-example-relational -p tests up -d --build && docker compose -f docker-compose.relational.test.yaml -p tests exec api /opt/wait-for-it.sh -t 0 localhost:3000 -- npm run test:e2e -- --watchAll --runInBand && docker compose -f docker-compose.relational.test.yaml -p tests down && docker compose -p tests rm -svf",
+ "prepare": "is-ci || husky",
+ "release": "release-it"
},
"dependencies": {
- "@nestjs/common": "^7.0.0",
- "@nestjs/core": "^7.0.0",
- "@nestjs/platform-express": "^7.0.0",
- "reflect-metadata": "^0.1.13",
- "rimraf": "^3.0.2",
- "rxjs": "^6.5.4"
+ "@aws-sdk/client-s3": "3.569.0",
+ "@aws-sdk/s3-request-presigner": "3.569.0",
+ "@nestjs/common": "10.3.8",
+ "@nestjs/config": "3.2.2",
+ "@nestjs/core": "10.3.8",
+ "@nestjs/jwt": "10.2.0",
+ "@nestjs/mongoose": "10.0.6",
+ "@nestjs/passport": "10.0.3",
+ "@nestjs/platform-express": "10.3.8",
+ "@nestjs/swagger": "7.3.1",
+ "@nestjs/typeorm": "10.0.2",
+ "@types/multer-s3": "3.0.3",
+ "@types/prompts": "2.4.9",
+ "apple-signin-auth": "1.7.6",
+ "bcryptjs": "2.4.3",
+ "class-transformer": "0.5.1",
+ "class-validator": "0.14.1",
+ "dotenv": "16.4.5",
+ "fb": "2.0.0",
+ "google-auth-library": "9.10.0",
+ "handlebars": "4.7.8",
+ "mongoose": "8.3.4",
+ "ms": "2.1.3",
+ "multer": "1.4.5-lts.1",
+ "multer-s3": "3.0.1",
+ "nestjs-i18n": "10.4.5",
+ "nodemailer": "6.9.13",
+ "passport": "0.7.0",
+ "passport-anonymous": "1.0.1",
+ "passport-jwt": "4.0.1",
+ "pg": "8.11.5",
+ "reflect-metadata": "0.2.2",
+ "rimraf": "5.0.7",
+ "rxjs": "7.8.1",
+ "source-map-support": "0.5.21",
+ "swagger-ui-express": "5.0.0",
+ "twitter": "1.7.1",
+ "typeorm": "0.3.20"
},
"devDependencies": {
- "@nestjs/cli": "^7.0.0",
- "@nestjs/schematics": "^7.0.0",
- "@nestjs/testing": "^7.0.0",
- "@types/express": "^4.17.3",
- "@types/jest": "26.0.10",
- "@types/node": "^13.9.1",
- "@types/supertest": "^2.0.8",
- "@typescript-eslint/eslint-plugin": "3.9.1",
- "@typescript-eslint/parser": "3.9.1",
- "eslint": "7.7.0",
- "eslint-config-prettier": "^6.10.0",
- "eslint-plugin-import": "^2.20.1",
- "jest": "26.4.2",
- "prettier": "^1.19.1",
- "supertest": "^4.0.2",
- "ts-jest": "26.2.0",
- "ts-loader": "^6.2.1",
- "ts-node": "9.0.0",
- "tsconfig-paths": "^3.9.0",
- "typescript": "^3.7.4"
+ "@commitlint/cli": "19.3.0",
+ "@commitlint/config-conventional": "19.2.2",
+ "@nestjs/cli": "10.3.2",
+ "@nestjs/schematics": "10.1.1",
+ "@nestjs/testing": "10.3.8",
+ "@release-it/conventional-changelog": "8.0.1",
+ "@swc/cli": "0.3.12",
+ "@swc/core": "1.5.6",
+ "@types/bcryptjs": "2.4.6",
+ "@types/express": "4.17.21",
+ "@types/facebook-js-sdk": "3.3.11",
+ "@types/jest": "29.5.12",
+ "@types/ms": "0.7.34",
+ "@types/multer": "1.4.11",
+ "@types/node": "20.12.8",
+ "@types/passport-anonymous": "1.0.5",
+ "@types/passport-jwt": "4.0.1",
+ "@types/supertest": "6.0.2",
+ "@types/twitter": "1.7.4",
+ "@typescript-eslint/eslint-plugin": "7.8.0",
+ "@typescript-eslint/parser": "7.8.0",
+ "env-cmd": "10.1.0",
+ "eslint": "8.57.0",
+ "eslint-config-prettier": "9.1.0",
+ "eslint-plugin-import": "2.29.1",
+ "eslint-plugin-prettier": "5.1.3",
+ "husky": "9.0.11",
+ "hygen": "6.2.11",
+ "is-ci": "3.0.1",
+ "jest": "29.7.0",
+ "prettier": "3.2.5",
+ "prompts": "2.4.2",
+ "release-it": "17.2.1",
+ "supertest": "7.0.0",
+ "ts-jest": "29.1.2",
+ "ts-loader": "9.5.1",
+ "ts-node": "10.9.2",
+ "tsconfig-paths": "4.2.0",
+ "tslib": "2.6.2",
+ "typescript": "5.4.5"
},
"jest": {
"moduleFileExtensions": [
@@ -57,11 +125,79 @@
"ts"
],
"rootDir": "src",
- "testRegex": ".spec.ts$",
+ "testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
+ "collectCoverageFrom": [
+ "**/*.(t|j)s"
+ ],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
+ },
+ "engines": {
+ "node": ">=16.0.0",
+ "npm": ">=8.0.0"
+ },
+ "release-it": {
+ "git": {
+ "commitMessage": "chore: release v${version}"
+ },
+ "github": {
+ "release": true
+ },
+ "npm": {
+ "publish": false
+ },
+ "plugins": {
+ "@release-it/conventional-changelog": {
+ "infile": "CHANGELOG.md",
+ "preset": {
+ "name": "conventionalcommits",
+ "types": [
+ {
+ "type": "chore(deps)",
+ "section": "Dependency Upgrades"
+ },
+ {
+ "type": "fix(deps)",
+ "section": "Dependency Upgrades"
+ },
+ {
+ "type": "feat",
+ "section": "Features"
+ },
+ {
+ "type": "fix",
+ "section": "Bug Fixes"
+ },
+ {
+ "type": "perf",
+ "section": "Performance Improvements"
+ },
+ {
+ "type": "revert",
+ "section": "Reverts"
+ },
+ {
+ "type": "docs",
+ "section": "Documentation"
+ },
+ {
+ "type": "refactor",
+ "section": "Code Refactoring"
+ },
+ {
+ "type": "test",
+ "section": "Tests"
+ },
+ {
+ "type": "ci",
+ "section": "Continuous Integration"
+ }
+ ]
+ }
+ }
+ }
}
}
diff --git a/relational.e2e.Dockerfile b/relational.e2e.Dockerfile
new file mode 100644
index 0000000..4040687
--- /dev/null
+++ b/relational.e2e.Dockerfile
@@ -0,0 +1,22 @@
+FROM node:20.12.2-alpine
+
+RUN apk add --no-cache bash
+RUN npm i -g @nestjs/cli typescript ts-node
+
+COPY package*.json /tmp/app/
+RUN cd /tmp/app && npm install
+
+COPY . /usr/src/app
+RUN cp -a /tmp/app/node_modules /usr/src/app
+COPY ./wait-for-it.sh /opt/wait-for-it.sh
+RUN chmod +x /opt/wait-for-it.sh
+COPY ./startup.relational.ci.sh /opt/startup.relational.ci.sh
+RUN chmod +x /opt/startup.relational.ci.sh
+RUN sed -i 's/\r//g' /opt/wait-for-it.sh
+RUN sed -i 's/\r//g' /opt/startup.relational.ci.sh
+
+WORKDIR /usr/src/app
+RUN echo "" > .env
+RUN npm run build
+
+CMD ["/opt/startup.relational.ci.sh"]
diff --git a/relational.test.Dockerfile b/relational.test.Dockerfile
new file mode 100644
index 0000000..1bd39ef
--- /dev/null
+++ b/relational.test.Dockerfile
@@ -0,0 +1,22 @@
+FROM node:20.12.2-alpine
+
+RUN apk add --no-cache bash
+RUN npm i -g @nestjs/cli typescript ts-node
+
+COPY package*.json /tmp/app/
+RUN cd /tmp/app && npm install
+
+COPY . /usr/src/app
+
+COPY ./wait-for-it.sh /opt/wait-for-it.sh
+RUN chmod +x /opt/wait-for-it.sh
+COPY ./startup.relational.test.sh /opt/startup.relational.test.sh
+RUN chmod +x /opt/startup.relational.test.sh
+RUN sed -i 's/\r//g' /opt/wait-for-it.sh
+RUN sed -i 's/\r//g' /opt/startup.relational.test.sh
+
+WORKDIR /usr/src/app
+
+RUN echo "" > .env
+
+CMD ["/opt/startup.relational.test.sh"]
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..f45d8f1
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "config:base"
+ ]
+}
diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts
deleted file mode 100644
index d22f389..0000000
--- a/src/app.controller.spec.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { AppController } from './app.controller';
-import { AppService } from './app.service';
-
-describe('AppController', () => {
- let appController: AppController;
-
- beforeEach(async () => {
- const app: TestingModule = await Test.createTestingModule({
- controllers: [AppController],
- providers: [AppService],
- }).compile();
-
- appController = app.get(AppController);
- });
-
- describe('root', () => {
- it('should return "Hello World!"', () => {
- expect(appController.getHello()).toBe('Hello World!');
- });
- });
-});
diff --git a/src/app.controller.ts b/src/app.controller.ts
deleted file mode 100644
index cce879e..0000000
--- a/src/app.controller.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Controller, Get } from '@nestjs/common';
-import { AppService } from './app.service';
-
-@Controller()
-export class AppController {
- constructor(private readonly appService: AppService) {}
-
- @Get()
- getHello(): string {
- return this.appService.getHello();
- }
-}
diff --git a/src/app.module.ts b/src/app.module.ts
index 8662803..8a03601 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -1,10 +1,102 @@
import { Module } from '@nestjs/common';
-import { AppController } from './app.controller';
-import { AppService } from './app.service';
+import { UsersModule } from './users/users.module';
+import { FilesModule } from './files/files.module';
+import { AuthModule } from './auth/auth.module';
+import databaseConfig from './database/config/database.config';
+import authConfig from './auth/config/auth.config';
+import appConfig from './config/app.config';
+import mailConfig from './mail/config/mail.config';
+import fileConfig from './files/config/file.config';
+import facebookConfig from './auth-facebook/config/facebook.config';
+import googleConfig from './auth-google/config/google.config';
+import twitterConfig from './auth-twitter/config/twitter.config';
+import appleConfig from './auth-apple/config/apple.config';
+import path from 'path';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { AuthAppleModule } from './auth-apple/auth-apple.module';
+import { AuthFacebookModule } from './auth-facebook/auth-facebook.module';
+import { AuthGoogleModule } from './auth-google/auth-google.module';
+import { AuthTwitterModule } from './auth-twitter/auth-twitter.module';
+import { I18nModule } from 'nestjs-i18n/dist/i18n.module';
+import { HeaderResolver } from 'nestjs-i18n';
+import { TypeOrmConfigService } from './database/typeorm-config.service';
+import { MailModule } from './mail/mail.module';
+import { HomeModule } from './home/home.module';
+import { DataSource, DataSourceOptions } from 'typeorm';
+import { AllConfigType } from './config/config.type';
+import { SessionModule } from './session/session.module';
+import { MailerModule } from './mailer/mailer.module';
+import { MongooseModule } from '@nestjs/mongoose';
+import { MongooseConfigService } from './database/mongoose-config.service';
+import { DatabaseConfig } from './database/config/database-config.type';
+
+//
+const infrastructureDatabaseModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? MongooseModule.forRootAsync({
+ useClass: MongooseConfigService,
+ })
+ : TypeOrmModule.forRootAsync({
+ useClass: TypeOrmConfigService,
+ dataSourceFactory: async (options: DataSourceOptions) => {
+ return new DataSource(options).initialize();
+ },
+ });
+//
@Module({
- imports: [],
- controllers: [AppController],
- providers: [AppService],
+ imports: [
+ ConfigModule.forRoot({
+ isGlobal: true,
+ load: [
+ databaseConfig,
+ authConfig,
+ appConfig,
+ mailConfig,
+ fileConfig,
+ facebookConfig,
+ googleConfig,
+ twitterConfig,
+ appleConfig,
+ ],
+ envFilePath: ['.env'],
+ }),
+ infrastructureDatabaseModule,
+ I18nModule.forRootAsync({
+ useFactory: (configService: ConfigService) => ({
+ fallbackLanguage: configService.getOrThrow('app.fallbackLanguage', {
+ infer: true,
+ }),
+ loaderOptions: { path: path.join(__dirname, '/i18n/'), watch: true },
+ }),
+ resolvers: [
+ {
+ use: HeaderResolver,
+ useFactory: (configService: ConfigService) => {
+ return [
+ configService.get('app.headerLanguage', {
+ infer: true,
+ }),
+ ];
+ },
+ inject: [ConfigService],
+ },
+ ],
+ imports: [ConfigModule],
+ inject: [ConfigService],
+ }),
+ UsersModule,
+ FilesModule,
+ AuthModule,
+ AuthFacebookModule,
+ AuthGoogleModule,
+ AuthTwitterModule,
+ AuthAppleModule,
+ SessionModule,
+ MailModule,
+ MailerModule,
+ HomeModule,
+ ],
})
export class AppModule {}
diff --git a/src/auth-apple/auth-apple.controller.ts b/src/auth-apple/auth-apple.controller.ts
new file mode 100644
index 0000000..f021d50
--- /dev/null
+++ b/src/auth-apple/auth-apple.controller.ts
@@ -0,0 +1,39 @@
+import {
+ Body,
+ Controller,
+ HttpCode,
+ HttpStatus,
+ Post,
+ SerializeOptions,
+} from '@nestjs/common';
+import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
+import { AuthService } from '../auth/auth.service';
+import { AuthAppleService } from './auth-apple.service';
+import { AuthAppleLoginDto } from './dto/auth-apple-login.dto';
+import { LoginResponseDto } from '../auth/dto/login-response.dto';
+
+@ApiTags('Auth')
+@Controller({
+ path: 'auth/apple',
+ version: '1',
+})
+export class AuthAppleController {
+ constructor(
+ private readonly authService: AuthService,
+ private readonly authAppleService: AuthAppleService,
+ ) {}
+
+ @ApiOkResponse({
+ type: LoginResponseDto,
+ })
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Post('login')
+ @HttpCode(HttpStatus.OK)
+ async login(@Body() loginDto: AuthAppleLoginDto): Promise {
+ const socialData = await this.authAppleService.getProfileByToken(loginDto);
+
+ return this.authService.validateSocialLogin('apple', socialData);
+ }
+}
diff --git a/src/auth-apple/auth-apple.module.ts b/src/auth-apple/auth-apple.module.ts
new file mode 100644
index 0000000..8d620ff
--- /dev/null
+++ b/src/auth-apple/auth-apple.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { AuthAppleService } from './auth-apple.service';
+import { ConfigModule } from '@nestjs/config';
+import { AuthAppleController } from './auth-apple.controller';
+import { AuthModule } from '../auth/auth.module';
+
+@Module({
+ imports: [ConfigModule, AuthModule],
+ providers: [AuthAppleService],
+ exports: [AuthAppleService],
+ controllers: [AuthAppleController],
+})
+export class AuthAppleModule {}
diff --git a/src/auth-apple/auth-apple.service.ts b/src/auth-apple/auth-apple.service.ts
new file mode 100644
index 0000000..0e98508
--- /dev/null
+++ b/src/auth-apple/auth-apple.service.ts
@@ -0,0 +1,26 @@
+import { Injectable } from '@nestjs/common';
+import appleSigninAuth from 'apple-signin-auth';
+import { ConfigService } from '@nestjs/config';
+import { SocialInterface } from '../social/interfaces/social.interface';
+import { AuthAppleLoginDto } from './dto/auth-apple-login.dto';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class AuthAppleService {
+ constructor(private configService: ConfigService) {}
+
+ async getProfileByToken(
+ loginDto: AuthAppleLoginDto,
+ ): Promise {
+ const data = await appleSigninAuth.verifyIdToken(loginDto.idToken, {
+ audience: this.configService.get('apple.appAudience', { infer: true }),
+ });
+
+ return {
+ id: data.sub,
+ email: data.email,
+ firstName: loginDto.firstName,
+ lastName: loginDto.lastName,
+ };
+ }
+}
diff --git a/src/auth-apple/config/apple-config.type.ts b/src/auth-apple/config/apple-config.type.ts
new file mode 100644
index 0000000..5099373
--- /dev/null
+++ b/src/auth-apple/config/apple-config.type.ts
@@ -0,0 +1,3 @@
+export type AppleConfig = {
+ appAudience: string[];
+};
diff --git a/src/auth-apple/config/apple.config.ts b/src/auth-apple/config/apple.config.ts
new file mode 100644
index 0000000..bdae386
--- /dev/null
+++ b/src/auth-apple/config/apple.config.ts
@@ -0,0 +1,19 @@
+import { registerAs } from '@nestjs/config';
+
+import { IsJSON, IsOptional } from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { AppleConfig } from './apple-config.type';
+
+class EnvironmentVariablesValidator {
+ @IsJSON()
+ @IsOptional()
+ APPLE_APP_AUDIENCE: string;
+}
+
+export default registerAs('apple', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ appAudience: JSON.parse(process.env.APPLE_APP_AUDIENCE ?? '[]'),
+ };
+});
diff --git a/src/auth-apple/dto/auth-apple-login.dto.ts b/src/auth-apple/dto/auth-apple-login.dto.ts
new file mode 100644
index 0000000..bb31181
--- /dev/null
+++ b/src/auth-apple/dto/auth-apple-login.dto.ts
@@ -0,0 +1,16 @@
+import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
+import { Allow, IsNotEmpty } from 'class-validator';
+
+export class AuthAppleLoginDto {
+ @ApiProperty({ example: 'abc' })
+ @IsNotEmpty()
+ idToken: string;
+
+ @Allow()
+ @ApiPropertyOptional()
+ firstName?: string;
+
+ @Allow()
+ @ApiPropertyOptional()
+ lastName?: string;
+}
diff --git a/src/auth-facebook/auth-facebook.controller.ts b/src/auth-facebook/auth-facebook.controller.ts
new file mode 100644
index 0000000..2ca5400
--- /dev/null
+++ b/src/auth-facebook/auth-facebook.controller.ts
@@ -0,0 +1,42 @@
+import {
+ Body,
+ Controller,
+ HttpCode,
+ HttpStatus,
+ Post,
+ SerializeOptions,
+} from '@nestjs/common';
+import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
+import { AuthService } from '../auth/auth.service';
+import { AuthFacebookService } from './auth-facebook.service';
+import { AuthFacebookLoginDto } from './dto/auth-facebook-login.dto';
+import { LoginResponseDto } from '../auth/dto/login-response.dto';
+
+@ApiTags('Auth')
+@Controller({
+ path: 'auth/facebook',
+ version: '1',
+})
+export class AuthFacebookController {
+ constructor(
+ private readonly authService: AuthService,
+ private readonly authFacebookService: AuthFacebookService,
+ ) {}
+
+ @ApiOkResponse({
+ type: LoginResponseDto,
+ })
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Post('login')
+ @HttpCode(HttpStatus.OK)
+ async login(
+ @Body() loginDto: AuthFacebookLoginDto,
+ ): Promise {
+ const socialData =
+ await this.authFacebookService.getProfileByToken(loginDto);
+
+ return this.authService.validateSocialLogin('facebook', socialData);
+ }
+}
diff --git a/src/auth-facebook/auth-facebook.module.ts b/src/auth-facebook/auth-facebook.module.ts
new file mode 100644
index 0000000..28e7e96
--- /dev/null
+++ b/src/auth-facebook/auth-facebook.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { AuthFacebookService } from './auth-facebook.service';
+import { ConfigModule } from '@nestjs/config';
+import { AuthFacebookController } from './auth-facebook.controller';
+import { AuthModule } from '../auth/auth.module';
+
+@Module({
+ imports: [ConfigModule, AuthModule],
+ providers: [AuthFacebookService],
+ exports: [AuthFacebookService],
+ controllers: [AuthFacebookController],
+})
+export class AuthFacebookModule {}
diff --git a/src/auth-facebook/auth-facebook.service.ts b/src/auth-facebook/auth-facebook.service.ts
new file mode 100644
index 0000000..a2dd867
--- /dev/null
+++ b/src/auth-facebook/auth-facebook.service.ts
@@ -0,0 +1,45 @@
+import { Injectable } from '@nestjs/common';
+import { Facebook } from 'fb';
+import { ConfigService } from '@nestjs/config';
+import { SocialInterface } from '../social/interfaces/social.interface';
+import { FacebookInterface } from './interfaces/facebook.interface';
+import { AuthFacebookLoginDto } from './dto/auth-facebook-login.dto';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class AuthFacebookService {
+ constructor(private configService: ConfigService) {}
+
+ async getProfileByToken(
+ loginDto: AuthFacebookLoginDto,
+ ): Promise {
+ const fb: Facebook = new Facebook({
+ appId: this.configService.get('facebook.appId', {
+ infer: true,
+ }),
+ appSecret: this.configService.get('facebook.appSecret', {
+ infer: true,
+ }),
+ version: 'v7.0',
+ });
+ fb.setAccessToken(loginDto.accessToken);
+
+ const data: FacebookInterface = await new Promise((resolve) => {
+ fb.api(
+ '/me',
+ 'get',
+ { fields: 'id,last_name,email,first_name' },
+ (response) => {
+ resolve(response);
+ },
+ );
+ });
+
+ return {
+ id: data.id,
+ email: data.email,
+ firstName: data.first_name,
+ lastName: data.last_name,
+ };
+ }
+}
diff --git a/src/auth-facebook/config/facebook-config.type.ts b/src/auth-facebook/config/facebook-config.type.ts
new file mode 100644
index 0000000..c4c16fc
--- /dev/null
+++ b/src/auth-facebook/config/facebook-config.type.ts
@@ -0,0 +1,4 @@
+export type FacebookConfig = {
+ appId?: string;
+ appSecret?: string;
+};
diff --git a/src/auth-facebook/config/facebook.config.ts b/src/auth-facebook/config/facebook.config.ts
new file mode 100644
index 0000000..5bfaf46
--- /dev/null
+++ b/src/auth-facebook/config/facebook.config.ts
@@ -0,0 +1,24 @@
+import { registerAs } from '@nestjs/config';
+
+import { IsOptional, IsString } from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { FacebookConfig } from './facebook-config.type';
+
+class EnvironmentVariablesValidator {
+ @IsString()
+ @IsOptional()
+ FACEBOOK_APP_ID: string;
+
+ @IsString()
+ @IsOptional()
+ FACEBOOK_APP_SECRET: string;
+}
+
+export default registerAs('facebook', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ appId: process.env.FACEBOOK_APP_ID,
+ appSecret: process.env.FACEBOOK_APP_SECRET,
+ };
+});
diff --git a/src/auth-facebook/dto/auth-facebook-login.dto.ts b/src/auth-facebook/dto/auth-facebook-login.dto.ts
new file mode 100644
index 0000000..e9f2735
--- /dev/null
+++ b/src/auth-facebook/dto/auth-facebook-login.dto.ts
@@ -0,0 +1,8 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty } from 'class-validator';
+
+export class AuthFacebookLoginDto {
+ @ApiProperty({ example: 'abc' })
+ @IsNotEmpty()
+ accessToken: string;
+}
diff --git a/src/auth-facebook/interfaces/facebook.interface.ts b/src/auth-facebook/interfaces/facebook.interface.ts
new file mode 100644
index 0000000..054cffe
--- /dev/null
+++ b/src/auth-facebook/interfaces/facebook.interface.ts
@@ -0,0 +1,6 @@
+export interface FacebookInterface {
+ id: string;
+ first_name?: string;
+ last_name?: string;
+ email?: string;
+}
diff --git a/src/auth-google/auth-google.controller.ts b/src/auth-google/auth-google.controller.ts
new file mode 100644
index 0000000..d55f4d5
--- /dev/null
+++ b/src/auth-google/auth-google.controller.ts
@@ -0,0 +1,39 @@
+import {
+ Body,
+ Controller,
+ HttpCode,
+ HttpStatus,
+ Post,
+ SerializeOptions,
+} from '@nestjs/common';
+import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
+import { AuthService } from '../auth/auth.service';
+import { AuthGoogleService } from './auth-google.service';
+import { AuthGoogleLoginDto } from './dto/auth-google-login.dto';
+import { LoginResponseDto } from '../auth/dto/login-response.dto';
+
+@ApiTags('Auth')
+@Controller({
+ path: 'auth/google',
+ version: '1',
+})
+export class AuthGoogleController {
+ constructor(
+ private readonly authService: AuthService,
+ private readonly authGoogleService: AuthGoogleService,
+ ) {}
+
+ @ApiOkResponse({
+ type: LoginResponseDto,
+ })
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Post('login')
+ @HttpCode(HttpStatus.OK)
+ async login(@Body() loginDto: AuthGoogleLoginDto): Promise {
+ const socialData = await this.authGoogleService.getProfileByToken(loginDto);
+
+ return this.authService.validateSocialLogin('google', socialData);
+ }
+}
diff --git a/src/auth-google/auth-google.module.ts b/src/auth-google/auth-google.module.ts
new file mode 100644
index 0000000..fa49ea2
--- /dev/null
+++ b/src/auth-google/auth-google.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { AuthGoogleService } from './auth-google.service';
+import { ConfigModule } from '@nestjs/config';
+import { AuthGoogleController } from './auth-google.controller';
+import { AuthModule } from '../auth/auth.module';
+
+@Module({
+ imports: [ConfigModule, AuthModule],
+ providers: [AuthGoogleService],
+ exports: [AuthGoogleService],
+ controllers: [AuthGoogleController],
+})
+export class AuthGoogleModule {}
diff --git a/src/auth-google/auth-google.service.ts b/src/auth-google/auth-google.service.ts
new file mode 100644
index 0000000..e24ea28
--- /dev/null
+++ b/src/auth-google/auth-google.service.ts
@@ -0,0 +1,51 @@
+import {
+ HttpStatus,
+ Injectable,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { OAuth2Client } from 'google-auth-library';
+import { SocialInterface } from '../social/interfaces/social.interface';
+import { AuthGoogleLoginDto } from './dto/auth-google-login.dto';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class AuthGoogleService {
+ private google: OAuth2Client;
+
+ constructor(private configService: ConfigService) {
+ this.google = new OAuth2Client(
+ configService.get('google.clientId', { infer: true }),
+ configService.get('google.clientSecret', { infer: true }),
+ );
+ }
+
+ async getProfileByToken(
+ loginDto: AuthGoogleLoginDto,
+ ): Promise {
+ const ticket = await this.google.verifyIdToken({
+ idToken: loginDto.idToken,
+ audience: [
+ this.configService.getOrThrow('google.clientId', { infer: true }),
+ ],
+ });
+
+ const data = ticket.getPayload();
+
+ if (!data) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ user: 'wrongToken',
+ },
+ });
+ }
+
+ return {
+ id: data.sub,
+ email: data.email,
+ firstName: data.given_name,
+ lastName: data.family_name,
+ };
+ }
+}
diff --git a/src/auth-google/config/google-config.type.ts b/src/auth-google/config/google-config.type.ts
new file mode 100644
index 0000000..5071db7
--- /dev/null
+++ b/src/auth-google/config/google-config.type.ts
@@ -0,0 +1,4 @@
+export type GoogleConfig = {
+ clientId?: string;
+ clientSecret?: string;
+};
diff --git a/src/auth-google/config/google.config.ts b/src/auth-google/config/google.config.ts
new file mode 100644
index 0000000..dc58e3b
--- /dev/null
+++ b/src/auth-google/config/google.config.ts
@@ -0,0 +1,24 @@
+import { registerAs } from '@nestjs/config';
+
+import { IsOptional, IsString } from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { GoogleConfig } from './google-config.type';
+
+class EnvironmentVariablesValidator {
+ @IsString()
+ @IsOptional()
+ GOOGLE_CLIENT_ID: string;
+
+ @IsString()
+ @IsOptional()
+ GOOGLE_CLIENT_SECRET: string;
+}
+
+export default registerAs('google', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ clientId: process.env.GOOGLE_CLIENT_ID,
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
+ };
+});
diff --git a/src/auth-google/dto/auth-google-login.dto.ts b/src/auth-google/dto/auth-google-login.dto.ts
new file mode 100644
index 0000000..959be16
--- /dev/null
+++ b/src/auth-google/dto/auth-google-login.dto.ts
@@ -0,0 +1,8 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty } from 'class-validator';
+
+export class AuthGoogleLoginDto {
+ @ApiProperty({ example: 'abc' })
+ @IsNotEmpty()
+ idToken: string;
+}
diff --git a/src/auth-twitter/auth-twitter.controller.ts b/src/auth-twitter/auth-twitter.controller.ts
new file mode 100644
index 0000000..b165f37
--- /dev/null
+++ b/src/auth-twitter/auth-twitter.controller.ts
@@ -0,0 +1,42 @@
+import {
+ Body,
+ Controller,
+ HttpCode,
+ HttpStatus,
+ Post,
+ SerializeOptions,
+} from '@nestjs/common';
+import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
+import { AuthService } from '../auth/auth.service';
+import { AuthTwitterService } from './auth-twitter.service';
+import { AuthTwitterLoginDto } from './dto/auth-twitter-login.dto';
+import { LoginResponseDto } from '../auth/dto/login-response.dto';
+
+@ApiTags('Auth')
+@Controller({
+ path: 'auth/twitter',
+ version: '1',
+})
+export class AuthTwitterController {
+ constructor(
+ private readonly authService: AuthService,
+ private readonly authTwitterService: AuthTwitterService,
+ ) {}
+
+ @ApiOkResponse({
+ type: LoginResponseDto,
+ })
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Post('login')
+ @HttpCode(HttpStatus.OK)
+ async login(
+ @Body() loginDto: AuthTwitterLoginDto,
+ ): Promise {
+ const socialData =
+ await this.authTwitterService.getProfileByToken(loginDto);
+
+ return this.authService.validateSocialLogin('twitter', socialData);
+ }
+}
diff --git a/src/auth-twitter/auth-twitter.module.ts b/src/auth-twitter/auth-twitter.module.ts
new file mode 100644
index 0000000..38d576d
--- /dev/null
+++ b/src/auth-twitter/auth-twitter.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { AuthTwitterService } from './auth-twitter.service';
+import { ConfigModule } from '@nestjs/config';
+import { AuthTwitterController } from './auth-twitter.controller';
+import { AuthModule } from '../auth/auth.module';
+
+@Module({
+ imports: [ConfigModule, AuthModule],
+ providers: [AuthTwitterService],
+ exports: [AuthTwitterService],
+ controllers: [AuthTwitterController],
+})
+export class AuthTwitterModule {}
diff --git a/src/auth-twitter/auth-twitter.service.ts b/src/auth-twitter/auth-twitter.service.ts
new file mode 100644
index 0000000..3a8fe79
--- /dev/null
+++ b/src/auth-twitter/auth-twitter.service.ts
@@ -0,0 +1,42 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import Twitter from 'twitter';
+import { SocialInterface } from '../social/interfaces/social.interface';
+import { AuthTwitterLoginDto } from './dto/auth-twitter-login.dto';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class AuthTwitterService {
+ constructor(private configService: ConfigService) {}
+
+ async getProfileByToken(
+ loginDto: AuthTwitterLoginDto,
+ ): Promise {
+ const twitter = new Twitter({
+ consumer_key: this.configService.getOrThrow('twitter.consumerKey', {
+ infer: true,
+ }),
+ consumer_secret: this.configService.getOrThrow('twitter.consumerSecret', {
+ infer: true,
+ }),
+ access_token_key: loginDto.accessTokenKey,
+ access_token_secret: loginDto.accessTokenSecret,
+ });
+
+ const data: Twitter.ResponseData = await new Promise((resolve) => {
+ twitter.get(
+ 'account/verify_credentials',
+ { include_email: true },
+ (error, profile) => {
+ resolve(profile);
+ },
+ );
+ });
+
+ return {
+ id: data.id?.toString(),
+ email: data.email,
+ firstName: data.name,
+ };
+ }
+}
diff --git a/src/auth-twitter/config/twitter-config.type.ts b/src/auth-twitter/config/twitter-config.type.ts
new file mode 100644
index 0000000..6b984e6
--- /dev/null
+++ b/src/auth-twitter/config/twitter-config.type.ts
@@ -0,0 +1,4 @@
+export type TwitterConfig = {
+ consumerKey?: string;
+ consumerSecret?: string;
+};
diff --git a/src/auth-twitter/config/twitter.config.ts b/src/auth-twitter/config/twitter.config.ts
new file mode 100644
index 0000000..75f472e
--- /dev/null
+++ b/src/auth-twitter/config/twitter.config.ts
@@ -0,0 +1,22 @@
+import { registerAs } from '@nestjs/config';
+import { IsString, IsOptional } from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+
+class EnvironmentVariablesValidator {
+ @IsString()
+ @IsOptional()
+ TWITTER_CONSUMER_KEY: string;
+
+ @IsString()
+ @IsOptional()
+ TWITTER_CONSUMER_SECRET: string;
+}
+
+export default registerAs('twitter', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ consumerKey: process.env.TWITTER_CONSUMER_KEY,
+ consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
+ };
+});
diff --git a/src/auth-twitter/dto/auth-twitter-login.dto.ts b/src/auth-twitter/dto/auth-twitter-login.dto.ts
new file mode 100644
index 0000000..2ecc42b
--- /dev/null
+++ b/src/auth-twitter/dto/auth-twitter-login.dto.ts
@@ -0,0 +1,12 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty } from 'class-validator';
+
+export class AuthTwitterLoginDto {
+ @ApiProperty({ example: 'abc' })
+ @IsNotEmpty()
+ accessTokenKey: string;
+
+ @ApiProperty({ example: 'abc' })
+ @IsNotEmpty()
+ accessTokenSecret: string;
+}
diff --git a/src/auth/auth-providers.enum.ts b/src/auth/auth-providers.enum.ts
new file mode 100644
index 0000000..cd4d92c
--- /dev/null
+++ b/src/auth/auth-providers.enum.ts
@@ -0,0 +1,7 @@
+export enum AuthProvidersEnum {
+ email = 'email',
+ facebook = 'facebook',
+ google = 'google',
+ twitter = 'twitter',
+ apple = 'apple',
+}
diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts
new file mode 100644
index 0000000..dff88f7
--- /dev/null
+++ b/src/auth/auth.controller.ts
@@ -0,0 +1,152 @@
+import {
+ Body,
+ Controller,
+ Get,
+ HttpCode,
+ HttpStatus,
+ Request,
+ Post,
+ UseGuards,
+ Patch,
+ Delete,
+ SerializeOptions,
+} from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger';
+import { AuthEmailLoginDto } from './dto/auth-email-login.dto';
+import { AuthForgotPasswordDto } from './dto/auth-forgot-password.dto';
+import { AuthConfirmEmailDto } from './dto/auth-confirm-email.dto';
+import { AuthResetPasswordDto } from './dto/auth-reset-password.dto';
+import { AuthUpdateDto } from './dto/auth-update.dto';
+import { AuthGuard } from '@nestjs/passport';
+import { AuthRegisterLoginDto } from './dto/auth-register-login.dto';
+import { LoginResponseDto } from './dto/login-response.dto';
+import { NullableType } from '../utils/types/nullable.type';
+import { User } from '../users/domain/user';
+import { RefreshResponseDto } from './dto/refresh-response.dto';
+
+@ApiTags('Auth')
+@Controller({
+ path: 'auth',
+ version: '1',
+})
+export class AuthController {
+ constructor(private readonly service: AuthService) {}
+
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Post('email/login')
+ @ApiOkResponse({
+ type: LoginResponseDto,
+ })
+ @HttpCode(HttpStatus.OK)
+ public login(@Body() loginDto: AuthEmailLoginDto): Promise {
+ return this.service.validateLogin(loginDto);
+ }
+
+ @Post('email/register')
+ @HttpCode(HttpStatus.NO_CONTENT)
+ async register(@Body() createUserDto: AuthRegisterLoginDto): Promise {
+ return this.service.register(createUserDto);
+ }
+
+ @Post('email/confirm')
+ @HttpCode(HttpStatus.NO_CONTENT)
+ async confirmEmail(
+ @Body() confirmEmailDto: AuthConfirmEmailDto,
+ ): Promise {
+ return this.service.confirmEmail(confirmEmailDto.hash);
+ }
+
+ @Post('email/confirm/new')
+ @HttpCode(HttpStatus.NO_CONTENT)
+ async confirmNewEmail(
+ @Body() confirmEmailDto: AuthConfirmEmailDto,
+ ): Promise {
+ return this.service.confirmNewEmail(confirmEmailDto.hash);
+ }
+
+ @Post('forgot/password')
+ @HttpCode(HttpStatus.NO_CONTENT)
+ async forgotPassword(
+ @Body() forgotPasswordDto: AuthForgotPasswordDto,
+ ): Promise {
+ return this.service.forgotPassword(forgotPasswordDto.email);
+ }
+
+ @Post('reset/password')
+ @HttpCode(HttpStatus.NO_CONTENT)
+ resetPassword(@Body() resetPasswordDto: AuthResetPasswordDto): Promise {
+ return this.service.resetPassword(
+ resetPasswordDto.hash,
+ resetPasswordDto.password,
+ );
+ }
+
+ @ApiBearerAuth()
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Get('me')
+ @UseGuards(AuthGuard('jwt'))
+ @ApiOkResponse({
+ type: User,
+ })
+ @HttpCode(HttpStatus.OK)
+ public me(@Request() request): Promise> {
+ return this.service.me(request.user);
+ }
+
+ @ApiBearerAuth()
+ @ApiOkResponse({
+ type: RefreshResponseDto,
+ })
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Post('refresh')
+ @UseGuards(AuthGuard('jwt-refresh'))
+ @HttpCode(HttpStatus.OK)
+ public refresh(@Request() request): Promise {
+ return this.service.refreshToken({
+ sessionId: request.user.sessionId,
+ hash: request.user.hash,
+ });
+ }
+
+ @ApiBearerAuth()
+ @Post('logout')
+ @UseGuards(AuthGuard('jwt'))
+ @HttpCode(HttpStatus.NO_CONTENT)
+ public async logout(@Request() request): Promise {
+ await this.service.logout({
+ sessionId: request.user.sessionId,
+ });
+ }
+
+ @ApiBearerAuth()
+ @SerializeOptions({
+ groups: ['me'],
+ })
+ @Patch('me')
+ @UseGuards(AuthGuard('jwt'))
+ @HttpCode(HttpStatus.OK)
+ @ApiOkResponse({
+ type: User,
+ })
+ public update(
+ @Request() request,
+ @Body() userDto: AuthUpdateDto,
+ ): Promise> {
+ return this.service.update(request.user, userDto);
+ }
+
+ @ApiBearerAuth()
+ @Delete('me')
+ @UseGuards(AuthGuard('jwt'))
+ @HttpCode(HttpStatus.NO_CONTENT)
+ public async delete(@Request() request): Promise {
+ return this.service.softDelete(request.user);
+ }
+}
diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts
new file mode 100644
index 0000000..ac7410e
--- /dev/null
+++ b/src/auth/auth.module.ts
@@ -0,0 +1,25 @@
+import { Module } from '@nestjs/common';
+import { AuthController } from './auth.controller';
+import { AuthService } from './auth.service';
+import { PassportModule } from '@nestjs/passport';
+import { JwtModule } from '@nestjs/jwt';
+import { JwtStrategy } from './strategies/jwt.strategy';
+import { AnonymousStrategy } from './strategies/anonymous.strategy';
+import { JwtRefreshStrategy } from './strategies/jwt-refresh.strategy';
+import { MailModule } from '../mail/mail.module';
+import { SessionModule } from '../session/session.module';
+import { UsersModule } from '../users/users.module';
+
+@Module({
+ imports: [
+ UsersModule,
+ SessionModule,
+ PassportModule,
+ MailModule,
+ JwtModule.register({}),
+ ],
+ controllers: [AuthController],
+ providers: [AuthService, JwtStrategy, JwtRefreshStrategy, AnonymousStrategy],
+ exports: [AuthService],
+})
+export class AuthModule {}
diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts
new file mode 100644
index 0000000..a75834b
--- /dev/null
+++ b/src/auth/auth.service.ts
@@ -0,0 +1,625 @@
+import {
+ HttpStatus,
+ Injectable,
+ NotFoundException,
+ UnauthorizedException,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import ms from 'ms';
+import crypto from 'crypto';
+import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
+import { JwtService } from '@nestjs/jwt';
+import bcrypt from 'bcryptjs';
+import { AuthEmailLoginDto } from './dto/auth-email-login.dto';
+import { AuthUpdateDto } from './dto/auth-update.dto';
+import { AuthProvidersEnum } from './auth-providers.enum';
+import { SocialInterface } from '../social/interfaces/social.interface';
+import { AuthRegisterLoginDto } from './dto/auth-register-login.dto';
+import { NullableType } from '../utils/types/nullable.type';
+import { LoginResponseDto } from './dto/login-response.dto';
+import { ConfigService } from '@nestjs/config';
+import { JwtRefreshPayloadType } from './strategies/types/jwt-refresh-payload.type';
+import { JwtPayloadType } from './strategies/types/jwt-payload.type';
+import { UsersService } from '../users/users.service';
+import { AllConfigType } from '../config/config.type';
+import { MailService } from '../mail/mail.service';
+import { RoleEnum } from '../roles/roles.enum';
+import { Session } from '../session/domain/session';
+import { SessionService } from '../session/session.service';
+import { StatusEnum } from '../statuses/statuses.enum';
+import { User } from '../users/domain/user';
+
+@Injectable()
+export class AuthService {
+ constructor(
+ private jwtService: JwtService,
+ private usersService: UsersService,
+ private sessionService: SessionService,
+ private mailService: MailService,
+ private configService: ConfigService,
+ ) {}
+
+ async validateLogin(loginDto: AuthEmailLoginDto): Promise {
+ const user = await this.usersService.findOne({
+ email: loginDto.email,
+ });
+
+ if (!user) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ email: 'notFound',
+ },
+ });
+ }
+
+ if (user.provider !== AuthProvidersEnum.email) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ email: `needLoginViaProvider:${user.provider}`,
+ },
+ });
+ }
+
+ if (!user.password) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ password: 'incorrectPassword',
+ },
+ });
+ }
+
+ const isValidPassword = await bcrypt.compare(
+ loginDto.password,
+ user.password,
+ );
+
+ if (!isValidPassword) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ password: 'incorrectPassword',
+ },
+ });
+ }
+
+ const hash = crypto
+ .createHash('sha256')
+ .update(randomStringGenerator())
+ .digest('hex');
+
+ const session = await this.sessionService.create({
+ user,
+ hash,
+ });
+
+ const { token, refreshToken, tokenExpires } = await this.getTokensData({
+ id: user.id,
+ role: user.role,
+ sessionId: session.id,
+ hash,
+ });
+
+ return {
+ refreshToken,
+ token,
+ tokenExpires,
+ user,
+ };
+ }
+
+ async validateSocialLogin(
+ authProvider: string,
+ socialData: SocialInterface,
+ ): Promise {
+ let user: NullableType = null;
+ const socialEmail = socialData.email?.toLowerCase();
+ let userByEmail: NullableType = null;
+
+ if (socialEmail) {
+ userByEmail = await this.usersService.findOne({
+ email: socialEmail,
+ });
+ }
+
+ if (socialData.id) {
+ user = await this.usersService.findOne({
+ socialId: socialData.id,
+ provider: authProvider,
+ });
+ }
+
+ if (user) {
+ if (socialEmail && !userByEmail) {
+ user.email = socialEmail;
+ }
+ await this.usersService.update(user.id, user);
+ } else if (userByEmail) {
+ user = userByEmail;
+ } else if (socialData.id) {
+ const role = {
+ id: RoleEnum.user,
+ };
+ const status = {
+ id: StatusEnum.active,
+ };
+
+ user = await this.usersService.create({
+ email: socialEmail ?? null,
+ firstName: socialData.firstName ?? null,
+ lastName: socialData.lastName ?? null,
+ socialId: socialData.id,
+ provider: authProvider,
+ role,
+ status,
+ });
+
+ user = await this.usersService.findOne({
+ id: user?.id,
+ });
+ }
+
+ if (!user) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ user: 'userNotFound',
+ },
+ });
+ }
+
+ const hash = crypto
+ .createHash('sha256')
+ .update(randomStringGenerator())
+ .digest('hex');
+
+ const session = await this.sessionService.create({
+ user,
+ hash,
+ });
+
+ const {
+ token: jwtToken,
+ refreshToken,
+ tokenExpires,
+ } = await this.getTokensData({
+ id: user.id,
+ role: user.role,
+ sessionId: session.id,
+ hash,
+ });
+
+ return {
+ refreshToken,
+ token: jwtToken,
+ tokenExpires,
+ user,
+ };
+ }
+
+ async register(dto: AuthRegisterLoginDto): Promise {
+ const user = await this.usersService.create({
+ ...dto,
+ email: dto.email,
+ role: {
+ id: RoleEnum.user,
+ },
+ status: {
+ id: StatusEnum.inactive,
+ },
+ });
+
+ const hash = await this.jwtService.signAsync(
+ {
+ confirmEmailUserId: user.id,
+ },
+ {
+ secret: this.configService.getOrThrow('auth.confirmEmailSecret', {
+ infer: true,
+ }),
+ expiresIn: this.configService.getOrThrow('auth.confirmEmailExpires', {
+ infer: true,
+ }),
+ },
+ );
+
+ await this.mailService.userSignUp({
+ to: dto.email,
+ data: {
+ hash,
+ },
+ });
+ }
+
+ async confirmEmail(hash: string): Promise {
+ let userId: User['id'];
+
+ try {
+ const jwtData = await this.jwtService.verifyAsync<{
+ confirmEmailUserId: User['id'];
+ }>(hash, {
+ secret: this.configService.getOrThrow('auth.confirmEmailSecret', {
+ infer: true,
+ }),
+ });
+
+ userId = jwtData.confirmEmailUserId;
+ } catch {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ hash: `invalidHash`,
+ },
+ });
+ }
+
+ const user = await this.usersService.findOne({
+ id: userId,
+ });
+
+ if (
+ !user ||
+ user?.status?.id?.toString() !== StatusEnum.inactive.toString()
+ ) {
+ throw new NotFoundException({
+ status: HttpStatus.NOT_FOUND,
+ error: `notFound`,
+ });
+ }
+
+ user.status = {
+ id: StatusEnum.active,
+ };
+
+ await this.usersService.update(user.id, user);
+ }
+
+ async confirmNewEmail(hash: string): Promise {
+ let userId: User['id'];
+ let newEmail: User['email'];
+
+ try {
+ const jwtData = await this.jwtService.verifyAsync<{
+ confirmEmailUserId: User['id'];
+ newEmail: User['email'];
+ }>(hash, {
+ secret: this.configService.getOrThrow('auth.confirmEmailSecret', {
+ infer: true,
+ }),
+ });
+
+ userId = jwtData.confirmEmailUserId;
+ newEmail = jwtData.newEmail;
+ } catch {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ hash: `invalidHash`,
+ },
+ });
+ }
+
+ const user = await this.usersService.findOne({
+ id: userId,
+ });
+
+ if (!user) {
+ throw new NotFoundException({
+ status: HttpStatus.NOT_FOUND,
+ error: `notFound`,
+ });
+ }
+
+ user.email = newEmail;
+ user.status = {
+ id: StatusEnum.active,
+ };
+
+ await this.usersService.update(user.id, user);
+ }
+
+ async forgotPassword(email: string): Promise {
+ const user = await this.usersService.findOne({
+ email,
+ });
+
+ if (!user) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ email: 'emailNotExists',
+ },
+ });
+ }
+
+ const tokenExpiresIn = this.configService.getOrThrow('auth.forgotExpires', {
+ infer: true,
+ });
+
+ const tokenExpires = Date.now() + ms(tokenExpiresIn);
+
+ const hash = await this.jwtService.signAsync(
+ {
+ forgotUserId: user.id,
+ },
+ {
+ secret: this.configService.getOrThrow('auth.forgotSecret', {
+ infer: true,
+ }),
+ expiresIn: tokenExpiresIn,
+ },
+ );
+
+ await this.mailService.forgotPassword({
+ to: email,
+ data: {
+ hash,
+ tokenExpires,
+ },
+ });
+ }
+
+ async resetPassword(hash: string, password: string): Promise {
+ let userId: User['id'];
+
+ try {
+ const jwtData = await this.jwtService.verifyAsync<{
+ forgotUserId: User['id'];
+ }>(hash, {
+ secret: this.configService.getOrThrow('auth.forgotSecret', {
+ infer: true,
+ }),
+ });
+
+ userId = jwtData.forgotUserId;
+ } catch {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ hash: `invalidHash`,
+ },
+ });
+ }
+
+ const user = await this.usersService.findOne({
+ id: userId,
+ });
+
+ if (!user) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ hash: `notFound`,
+ },
+ });
+ }
+
+ user.password = password;
+
+ await this.sessionService.softDelete({
+ user: {
+ id: user.id,
+ },
+ });
+
+ await this.usersService.update(user.id, user);
+ }
+
+ async me(userJwtPayload: JwtPayloadType): Promise> {
+ return this.usersService.findOne({
+ id: userJwtPayload.id,
+ });
+ }
+
+ async update(
+ userJwtPayload: JwtPayloadType,
+ userDto: AuthUpdateDto,
+ ): Promise> {
+ const currentUser = await this.usersService.findOne({
+ id: userJwtPayload.id,
+ });
+
+ if (!currentUser) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ user: 'userNotFound',
+ },
+ });
+ }
+
+ if (userDto.password) {
+ if (!userDto.oldPassword) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ oldPassword: 'missingOldPassword',
+ },
+ });
+ }
+
+ if (!currentUser.password) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ oldPassword: 'incorrectOldPassword',
+ },
+ });
+ }
+
+ const isValidOldPassword = await bcrypt.compare(
+ userDto.oldPassword,
+ currentUser.password,
+ );
+
+ if (!isValidOldPassword) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ oldPassword: 'incorrectOldPassword',
+ },
+ });
+ } else {
+ await this.sessionService.softDelete({
+ user: {
+ id: currentUser.id,
+ },
+ excludeId: userJwtPayload.sessionId,
+ });
+ }
+ }
+
+ if (userDto.email && userDto.email !== currentUser.email) {
+ const userByEmail = await this.usersService.findOne({
+ email: userDto.email,
+ });
+
+ if (userByEmail && userByEmail.id !== currentUser.id) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ email: 'emailExists',
+ },
+ });
+ }
+
+ const hash = await this.jwtService.signAsync(
+ {
+ confirmEmailUserId: currentUser.id,
+ newEmail: userDto.email,
+ },
+ {
+ secret: this.configService.getOrThrow('auth.confirmEmailSecret', {
+ infer: true,
+ }),
+ expiresIn: this.configService.getOrThrow('auth.confirmEmailExpires', {
+ infer: true,
+ }),
+ },
+ );
+
+ await this.mailService.confirmNewEmail({
+ to: userDto.email,
+ data: {
+ hash,
+ },
+ });
+ }
+
+ delete userDto.email;
+ delete userDto.oldPassword;
+
+ await this.usersService.update(userJwtPayload.id, userDto);
+
+ return this.usersService.findOne({
+ id: userJwtPayload.id,
+ });
+ }
+
+ async refreshToken(
+ data: Pick,
+ ): Promise> {
+ const session = await this.sessionService.findOne({
+ id: data.sessionId,
+ });
+
+ if (!session) {
+ throw new UnauthorizedException();
+ }
+
+ if (session.hash !== data.hash) {
+ throw new UnauthorizedException();
+ }
+
+ const hash = crypto
+ .createHash('sha256')
+ .update(randomStringGenerator())
+ .digest('hex');
+
+ const user = await this.usersService.findOne({
+ id: session.user.id,
+ });
+
+ if (!user?.role) {
+ throw new UnauthorizedException();
+ }
+
+ await this.sessionService.update(session.id, {
+ hash,
+ });
+
+ const { token, refreshToken, tokenExpires } = await this.getTokensData({
+ id: session.user.id,
+ role: {
+ id: user.role.id,
+ },
+ sessionId: session.id,
+ hash,
+ });
+
+ return {
+ token,
+ refreshToken,
+ tokenExpires,
+ };
+ }
+
+ async softDelete(user: User): Promise {
+ await this.usersService.softDelete(user.id);
+ }
+
+ async logout(data: Pick) {
+ return this.sessionService.softDelete({
+ id: data.sessionId,
+ });
+ }
+
+ private async getTokensData(data: {
+ id: User['id'];
+ role: User['role'];
+ sessionId: Session['id'];
+ hash: Session['hash'];
+ }) {
+ const tokenExpiresIn = this.configService.getOrThrow('auth.expires', {
+ infer: true,
+ });
+
+ const tokenExpires = Date.now() + ms(tokenExpiresIn);
+
+ const [token, refreshToken] = await Promise.all([
+ await this.jwtService.signAsync(
+ {
+ id: data.id,
+ role: data.role,
+ sessionId: data.sessionId,
+ },
+ {
+ secret: this.configService.getOrThrow('auth.secret', { infer: true }),
+ expiresIn: tokenExpiresIn,
+ },
+ ),
+ await this.jwtService.signAsync(
+ {
+ sessionId: data.sessionId,
+ hash: data.hash,
+ },
+ {
+ secret: this.configService.getOrThrow('auth.refreshSecret', {
+ infer: true,
+ }),
+ expiresIn: this.configService.getOrThrow('auth.refreshExpires', {
+ infer: true,
+ }),
+ },
+ ),
+ ]);
+
+ return {
+ token,
+ refreshToken,
+ tokenExpires,
+ };
+ }
+}
diff --git a/src/auth/config/auth-config.type.ts b/src/auth/config/auth-config.type.ts
new file mode 100644
index 0000000..50b245d
--- /dev/null
+++ b/src/auth/config/auth-config.type.ts
@@ -0,0 +1,10 @@
+export type AuthConfig = {
+ secret?: string;
+ expires?: string;
+ refreshSecret?: string;
+ refreshExpires?: string;
+ forgotSecret?: string;
+ forgotExpires?: string;
+ confirmEmailSecret?: string;
+ confirmEmailExpires?: string;
+};
diff --git a/src/auth/config/auth.config.ts b/src/auth/config/auth.config.ts
new file mode 100644
index 0000000..c5c0799
--- /dev/null
+++ b/src/auth/config/auth.config.ts
@@ -0,0 +1,46 @@
+import { registerAs } from '@nestjs/config';
+
+import { IsString } from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { AuthConfig } from './auth-config.type';
+
+class EnvironmentVariablesValidator {
+ @IsString()
+ AUTH_JWT_SECRET: string;
+
+ @IsString()
+ AUTH_JWT_TOKEN_EXPIRES_IN: string;
+
+ @IsString()
+ AUTH_REFRESH_SECRET: string;
+
+ @IsString()
+ AUTH_REFRESH_TOKEN_EXPIRES_IN: string;
+
+ @IsString()
+ AUTH_FORGOT_SECRET: string;
+
+ @IsString()
+ AUTH_FORGOT_TOKEN_EXPIRES_IN: string;
+
+ @IsString()
+ AUTH_CONFIRM_EMAIL_SECRET: string;
+
+ @IsString()
+ AUTH_CONFIRM_EMAIL_TOKEN_EXPIRES_IN: string;
+}
+
+export default registerAs('auth', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ secret: process.env.AUTH_JWT_SECRET,
+ expires: process.env.AUTH_JWT_TOKEN_EXPIRES_IN,
+ refreshSecret: process.env.AUTH_REFRESH_SECRET,
+ refreshExpires: process.env.AUTH_REFRESH_TOKEN_EXPIRES_IN,
+ forgotSecret: process.env.AUTH_FORGOT_SECRET,
+ forgotExpires: process.env.AUTH_FORGOT_TOKEN_EXPIRES_IN,
+ confirmEmailSecret: process.env.AUTH_CONFIRM_EMAIL_SECRET,
+ confirmEmailExpires: process.env.AUTH_CONFIRM_EMAIL_TOKEN_EXPIRES_IN,
+ };
+});
diff --git a/src/auth/dto/auth-confirm-email.dto.ts b/src/auth/dto/auth-confirm-email.dto.ts
new file mode 100644
index 0000000..93aabcd
--- /dev/null
+++ b/src/auth/dto/auth-confirm-email.dto.ts
@@ -0,0 +1,8 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty } from 'class-validator';
+
+export class AuthConfirmEmailDto {
+ @ApiProperty()
+ @IsNotEmpty()
+ hash: string;
+}
diff --git a/src/auth/dto/auth-email-login.dto.ts b/src/auth/dto/auth-email-login.dto.ts
new file mode 100644
index 0000000..cb1a166
--- /dev/null
+++ b/src/auth/dto/auth-email-login.dto.ts
@@ -0,0 +1,16 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsEmail, IsNotEmpty } from 'class-validator';
+import { Transform } from 'class-transformer';
+import { lowerCaseTransformer } from '../../utils/transformers/lower-case.transformer';
+
+export class AuthEmailLoginDto {
+ @ApiProperty({ example: 'test1@example.com', type: String })
+ @Transform(lowerCaseTransformer)
+ @IsEmail()
+ @IsNotEmpty()
+ email: string;
+
+ @ApiProperty()
+ @IsNotEmpty()
+ password: string;
+}
diff --git a/src/auth/dto/auth-forgot-password.dto.ts b/src/auth/dto/auth-forgot-password.dto.ts
new file mode 100644
index 0000000..2aac901
--- /dev/null
+++ b/src/auth/dto/auth-forgot-password.dto.ts
@@ -0,0 +1,11 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsEmail } from 'class-validator';
+import { Transform } from 'class-transformer';
+import { lowerCaseTransformer } from '../../utils/transformers/lower-case.transformer';
+
+export class AuthForgotPasswordDto {
+ @ApiProperty({ example: 'test1@example.com', type: String })
+ @Transform(lowerCaseTransformer)
+ @IsEmail()
+ email: string;
+}
diff --git a/src/auth/dto/auth-register-login.dto.ts b/src/auth/dto/auth-register-login.dto.ts
new file mode 100644
index 0000000..0a88afd
--- /dev/null
+++ b/src/auth/dto/auth-register-login.dto.ts
@@ -0,0 +1,23 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
+import { Transform } from 'class-transformer';
+import { lowerCaseTransformer } from '../../utils/transformers/lower-case.transformer';
+
+export class AuthRegisterLoginDto {
+ @ApiProperty({ example: 'test1@example.com', type: String })
+ @Transform(lowerCaseTransformer)
+ @IsEmail()
+ email: string;
+
+ @ApiProperty()
+ @MinLength(6)
+ password: string;
+
+ @ApiProperty({ example: 'John' })
+ @IsNotEmpty()
+ firstName: string;
+
+ @ApiProperty({ example: 'Doe' })
+ @IsNotEmpty()
+ lastName: string;
+}
diff --git a/src/auth/dto/auth-reset-password.dto.ts b/src/auth/dto/auth-reset-password.dto.ts
new file mode 100644
index 0000000..6ffd868
--- /dev/null
+++ b/src/auth/dto/auth-reset-password.dto.ts
@@ -0,0 +1,12 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty } from 'class-validator';
+
+export class AuthResetPasswordDto {
+ @ApiProperty()
+ @IsNotEmpty()
+ password: string;
+
+ @ApiProperty()
+ @IsNotEmpty()
+ hash: string;
+}
diff --git a/src/auth/dto/auth-update.dto.ts b/src/auth/dto/auth-update.dto.ts
new file mode 100644
index 0000000..08a1c2a
--- /dev/null
+++ b/src/auth/dto/auth-update.dto.ts
@@ -0,0 +1,39 @@
+import { ApiPropertyOptional } from '@nestjs/swagger';
+import { IsEmail, IsNotEmpty, IsOptional, MinLength } from 'class-validator';
+import { FileDto } from '../../files/dto/file.dto';
+import { Transform } from 'class-transformer';
+import { lowerCaseTransformer } from '../../utils/transformers/lower-case.transformer';
+
+export class AuthUpdateDto {
+ @ApiPropertyOptional({ type: () => FileDto })
+ @IsOptional()
+ photo?: FileDto | null;
+
+ @ApiPropertyOptional({ example: 'John' })
+ @IsOptional()
+ @IsNotEmpty({ message: 'mustBeNotEmpty' })
+ firstName?: string;
+
+ @ApiPropertyOptional({ example: 'Doe' })
+ @IsOptional()
+ @IsNotEmpty({ message: 'mustBeNotEmpty' })
+ lastName?: string;
+
+ @ApiPropertyOptional({ example: 'new.email@example.com' })
+ @IsOptional()
+ @IsNotEmpty()
+ @IsEmail()
+ @Transform(lowerCaseTransformer)
+ email?: string;
+
+ @ApiPropertyOptional()
+ @IsOptional()
+ @IsNotEmpty()
+ @MinLength(6)
+ password?: string;
+
+ @ApiPropertyOptional()
+ @IsOptional()
+ @IsNotEmpty({ message: 'mustBeNotEmpty' })
+ oldPassword?: string;
+}
diff --git a/src/auth/dto/login-response.dto.ts b/src/auth/dto/login-response.dto.ts
new file mode 100644
index 0000000..a48da6c
--- /dev/null
+++ b/src/auth/dto/login-response.dto.ts
@@ -0,0 +1,18 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { User } from '../../users/domain/user';
+
+export class LoginResponseDto {
+ @ApiResponseProperty()
+ token: string;
+
+ @ApiResponseProperty()
+ refreshToken: string;
+
+ @ApiResponseProperty()
+ tokenExpires: number;
+
+ @ApiResponseProperty({
+ type: () => User,
+ })
+ user: User;
+}
diff --git a/src/auth/dto/refresh-response.dto.ts b/src/auth/dto/refresh-response.dto.ts
new file mode 100644
index 0000000..68229a6
--- /dev/null
+++ b/src/auth/dto/refresh-response.dto.ts
@@ -0,0 +1,12 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+export class RefreshResponseDto {
+ @ApiResponseProperty()
+ token: string;
+
+ @ApiResponseProperty()
+ refreshToken: string;
+
+ @ApiResponseProperty()
+ tokenExpires: number;
+}
diff --git a/src/auth/strategies/anonymous.strategy.ts b/src/auth/strategies/anonymous.strategy.ts
new file mode 100644
index 0000000..d21bfc7
--- /dev/null
+++ b/src/auth/strategies/anonymous.strategy.ts
@@ -0,0 +1,14 @@
+import { Strategy } from 'passport-anonymous';
+import { Injectable } from '@nestjs/common';
+import { PassportStrategy } from '@nestjs/passport';
+
+@Injectable()
+export class AnonymousStrategy extends PassportStrategy(Strategy) {
+ constructor() {
+ super();
+ }
+
+ public validate(payload: unknown, request: unknown): unknown {
+ return request;
+ }
+}
diff --git a/src/auth/strategies/jwt-refresh.strategy.ts b/src/auth/strategies/jwt-refresh.strategy.ts
new file mode 100644
index 0000000..df8ab61
--- /dev/null
+++ b/src/auth/strategies/jwt-refresh.strategy.ts
@@ -0,0 +1,30 @@
+import { ExtractJwt, Strategy } from 'passport-jwt';
+import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { PassportStrategy } from '@nestjs/passport';
+import { ConfigService } from '@nestjs/config';
+import { JwtRefreshPayloadType } from './types/jwt-refresh-payload.type';
+import { OrNeverType } from '../../utils/types/or-never.type';
+import { AllConfigType } from '../../config/config.type';
+
+@Injectable()
+export class JwtRefreshStrategy extends PassportStrategy(
+ Strategy,
+ 'jwt-refresh',
+) {
+ constructor(configService: ConfigService) {
+ super({
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+ secretOrKey: configService.get('auth.refreshSecret', { infer: true }),
+ });
+ }
+
+ public validate(
+ payload: JwtRefreshPayloadType,
+ ): OrNeverType {
+ if (!payload.sessionId) {
+ throw new UnauthorizedException();
+ }
+
+ return payload;
+ }
+}
diff --git a/src/auth/strategies/jwt.strategy.ts b/src/auth/strategies/jwt.strategy.ts
new file mode 100644
index 0000000..f7f319e
--- /dev/null
+++ b/src/auth/strategies/jwt.strategy.ts
@@ -0,0 +1,27 @@
+import { ExtractJwt, Strategy } from 'passport-jwt';
+import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { PassportStrategy } from '@nestjs/passport';
+import { ConfigService } from '@nestjs/config';
+import { OrNeverType } from '../../utils/types/or-never.type';
+import { JwtPayloadType } from './types/jwt-payload.type';
+import { AllConfigType } from '../../config/config.type';
+
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
+ constructor(configService: ConfigService) {
+ super({
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+ secretOrKey: configService.get('auth.secret', { infer: true }),
+ });
+ }
+
+ // Why we don't check if the user exists in the database:
+ // https://github.com/khulnasoft/nestjs-template/blob/main/docs/auth.md#about-jwt-strategy
+ public validate(payload: JwtPayloadType): OrNeverType {
+ if (!payload.id) {
+ throw new UnauthorizedException();
+ }
+
+ return payload;
+ }
+}
diff --git a/src/auth/strategies/types/jwt-payload.type.ts b/src/auth/strategies/types/jwt-payload.type.ts
new file mode 100644
index 0000000..6cf5ef3
--- /dev/null
+++ b/src/auth/strategies/types/jwt-payload.type.ts
@@ -0,0 +1,8 @@
+import { Session } from '../../../session/domain/session';
+import { User } from '../../../users/domain/user';
+
+export type JwtPayloadType = Pick & {
+ sessionId: Session['id'];
+ iat: number;
+ exp: number;
+};
diff --git a/src/auth/strategies/types/jwt-refresh-payload.type.ts b/src/auth/strategies/types/jwt-refresh-payload.type.ts
new file mode 100644
index 0000000..c05b5a8
--- /dev/null
+++ b/src/auth/strategies/types/jwt-refresh-payload.type.ts
@@ -0,0 +1,8 @@
+import { Session } from '../../../session/domain/session';
+
+export type JwtRefreshPayloadType = {
+ sessionId: Session['id'];
+ hash: Session['hash'];
+ iat: number;
+ exp: number;
+};
diff --git a/src/config/app-config.type.ts b/src/config/app-config.type.ts
new file mode 100644
index 0000000..c6a5edf
--- /dev/null
+++ b/src/config/app-config.type.ts
@@ -0,0 +1,11 @@
+export type AppConfig = {
+ nodeEnv: string;
+ name: string;
+ workingDirectory: string;
+ frontendDomain?: string;
+ backendDomain: string;
+ port: number;
+ apiPrefix: string;
+ fallbackLanguage: string;
+ headerLanguage: string;
+};
diff --git a/src/config/app.config.ts b/src/config/app.config.ts
new file mode 100644
index 0000000..c4ae8b2
--- /dev/null
+++ b/src/config/app.config.ts
@@ -0,0 +1,70 @@
+import { registerAs } from '@nestjs/config';
+import { AppConfig } from './app-config.type';
+import validateConfig from '.././utils/validate-config';
+import {
+ IsEnum,
+ IsInt,
+ IsOptional,
+ IsString,
+ IsUrl,
+ Max,
+ Min,
+} from 'class-validator';
+
+enum Environment {
+ Development = 'development',
+ Production = 'production',
+ Test = 'test',
+}
+
+class EnvironmentVariablesValidator {
+ @IsEnum(Environment)
+ @IsOptional()
+ NODE_ENV: Environment;
+
+ @IsInt()
+ @Min(0)
+ @Max(65535)
+ @IsOptional()
+ APP_PORT: number;
+
+ @IsUrl({ require_tld: false })
+ @IsOptional()
+ FRONTEND_DOMAIN: string;
+
+ @IsUrl({ require_tld: false })
+ @IsOptional()
+ BACKEND_DOMAIN: string;
+
+ @IsString()
+ @IsOptional()
+ API_PREFIX: string;
+
+ @IsString()
+ @IsOptional()
+ APP_FALLBACK_LANGUAGE: string;
+
+ @IsString()
+ @IsOptional()
+ APP_HEADER_LANGUAGE: string;
+}
+
+export default registerAs('app', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ nodeEnv: process.env.NODE_ENV || 'development',
+ name: process.env.APP_NAME || 'app',
+ workingDirectory: process.env.PWD || process.cwd(),
+ frontendDomain: process.env.FRONTEND_DOMAIN,
+ backendDomain: process.env.BACKEND_DOMAIN ?? 'http://localhost',
+ port: process.env.APP_PORT
+ ? parseInt(process.env.APP_PORT, 10)
+ : process.env.PORT
+ ? parseInt(process.env.PORT, 10)
+ : 3000,
+ apiPrefix: process.env.API_PREFIX || 'api',
+ fallbackLanguage: process.env.APP_FALLBACK_LANGUAGE || 'en',
+ headerLanguage: process.env.APP_HEADER_LANGUAGE || 'x-custom-lang',
+ };
+});
diff --git a/src/config/config.type.ts b/src/config/config.type.ts
new file mode 100644
index 0000000..950e041
--- /dev/null
+++ b/src/config/config.type.ts
@@ -0,0 +1,21 @@
+import { AppConfig } from './app-config.type';
+import { AppleConfig } from '../auth-apple/config/apple-config.type';
+import { AuthConfig } from '../auth/config/auth-config.type';
+import { DatabaseConfig } from '../database/config/database-config.type';
+import { FacebookConfig } from '../auth-facebook/config/facebook-config.type';
+import { FileConfig } from '../files/config/file-config.type';
+import { GoogleConfig } from '../auth-google/config/google-config.type';
+import { MailConfig } from '../mail/config/mail-config.type';
+import { TwitterConfig } from '../auth-twitter/config/twitter-config.type';
+
+export type AllConfigType = {
+ app: AppConfig;
+ apple: AppleConfig;
+ auth: AuthConfig;
+ database: DatabaseConfig;
+ facebook: FacebookConfig;
+ file: FileConfig;
+ google: GoogleConfig;
+ mail: MailConfig;
+ twitter: TwitterConfig;
+};
diff --git a/src/database/config/database-config.type.ts b/src/database/config/database-config.type.ts
new file mode 100644
index 0000000..d958b45
--- /dev/null
+++ b/src/database/config/database-config.type.ts
@@ -0,0 +1,17 @@
+export type DatabaseConfig = {
+ isDocumentDatabase: boolean;
+ url?: string;
+ type?: string;
+ host?: string;
+ port?: number;
+ password?: string;
+ name?: string;
+ username?: string;
+ synchronize?: boolean;
+ maxConnections: number;
+ sslEnabled?: boolean;
+ rejectUnauthorized?: boolean;
+ ca?: string;
+ key?: string;
+ cert?: string;
+};
diff --git a/src/database/config/database.config.ts b/src/database/config/database.config.ts
new file mode 100644
index 0000000..e9cf549
--- /dev/null
+++ b/src/database/config/database.config.ts
@@ -0,0 +1,99 @@
+import { registerAs } from '@nestjs/config';
+
+import {
+ IsOptional,
+ IsInt,
+ Min,
+ Max,
+ IsString,
+ ValidateIf,
+ IsBoolean,
+} from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { DatabaseConfig } from './database-config.type';
+
+class EnvironmentVariablesValidator {
+ @ValidateIf((envValues) => envValues.DATABASE_URL)
+ @IsString()
+ DATABASE_URL: string;
+
+ @ValidateIf((envValues) => !envValues.DATABASE_URL)
+ @IsString()
+ DATABASE_TYPE: string;
+
+ @ValidateIf((envValues) => !envValues.DATABASE_URL)
+ @IsString()
+ DATABASE_HOST: string;
+
+ @ValidateIf((envValues) => !envValues.DATABASE_URL)
+ @IsInt()
+ @Min(0)
+ @Max(65535)
+ DATABASE_PORT: number;
+
+ @ValidateIf((envValues) => !envValues.DATABASE_URL)
+ @IsString()
+ DATABASE_PASSWORD: string;
+
+ @ValidateIf((envValues) => !envValues.DATABASE_URL)
+ @IsString()
+ DATABASE_NAME: string;
+
+ @ValidateIf((envValues) => !envValues.DATABASE_URL)
+ @IsString()
+ DATABASE_USERNAME: string;
+
+ @IsBoolean()
+ @IsOptional()
+ DATABASE_SYNCHRONIZE: boolean;
+
+ @IsInt()
+ @IsOptional()
+ DATABASE_MAX_CONNECTIONS: number;
+
+ @IsBoolean()
+ @IsOptional()
+ DATABASE_SSL_ENABLED: boolean;
+
+ @IsBoolean()
+ @IsOptional()
+ DATABASE_REJECT_UNAUTHORIZED: boolean;
+
+ @IsString()
+ @IsOptional()
+ DATABASE_CA: string;
+
+ @IsString()
+ @IsOptional()
+ DATABASE_KEY: string;
+
+ @IsString()
+ @IsOptional()
+ DATABASE_CERT: string;
+}
+
+export default registerAs('database', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ isDocumentDatabase: ['mongodb'].includes(process.env.DATABASE_TYPE ?? ''),
+ url: process.env.DATABASE_URL,
+ type: process.env.DATABASE_TYPE,
+ host: process.env.DATABASE_HOST,
+ port: process.env.DATABASE_PORT
+ ? parseInt(process.env.DATABASE_PORT, 10)
+ : 5432,
+ password: process.env.DATABASE_PASSWORD,
+ name: process.env.DATABASE_NAME,
+ username: process.env.DATABASE_USERNAME,
+ synchronize: process.env.DATABASE_SYNCHRONIZE === 'true',
+ maxConnections: process.env.DATABASE_MAX_CONNECTIONS
+ ? parseInt(process.env.DATABASE_MAX_CONNECTIONS, 10)
+ : 100,
+ sslEnabled: process.env.DATABASE_SSL_ENABLED === 'true',
+ rejectUnauthorized: process.env.DATABASE_REJECT_UNAUTHORIZED === 'true',
+ ca: process.env.DATABASE_CA,
+ key: process.env.DATABASE_KEY,
+ cert: process.env.DATABASE_CERT,
+ };
+});
diff --git a/src/database/data-source.ts b/src/database/data-source.ts
new file mode 100644
index 0000000..77b5eca
--- /dev/null
+++ b/src/database/data-source.ts
@@ -0,0 +1,42 @@
+import 'reflect-metadata';
+import { DataSource, DataSourceOptions } from 'typeorm';
+
+export const AppDataSource = new DataSource({
+ type: process.env.DATABASE_TYPE,
+ url: process.env.DATABASE_URL,
+ host: process.env.DATABASE_HOST,
+ port: process.env.DATABASE_PORT
+ ? parseInt(process.env.DATABASE_PORT, 10)
+ : 5432,
+ username: process.env.DATABASE_USERNAME,
+ password: process.env.DATABASE_PASSWORD,
+ database: process.env.DATABASE_NAME,
+ synchronize: process.env.DATABASE_SYNCHRONIZE === 'true',
+ dropSchema: false,
+ keepConnectionAlive: true,
+ logging: process.env.NODE_ENV !== 'production',
+ entities: [__dirname + '/../**/*.entity{.ts,.js}'],
+ migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
+ cli: {
+ entitiesDir: 'src',
+
+ subscribersDir: 'subscriber',
+ },
+ extra: {
+ // based on https://node-postgres.com/api/pool
+ // max connection pool size
+ max: process.env.DATABASE_MAX_CONNECTIONS
+ ? parseInt(process.env.DATABASE_MAX_CONNECTIONS, 10)
+ : 100,
+ ssl:
+ process.env.DATABASE_SSL_ENABLED === 'true'
+ ? {
+ rejectUnauthorized:
+ process.env.DATABASE_REJECT_UNAUTHORIZED === 'true',
+ ca: process.env.DATABASE_CA ?? undefined,
+ key: process.env.DATABASE_KEY ?? undefined,
+ cert: process.env.DATABASE_CERT ?? undefined,
+ }
+ : undefined,
+ },
+} as DataSourceOptions);
diff --git a/src/database/migrations/1715028537217-CreateUser.ts b/src/database/migrations/1715028537217-CreateUser.ts
new file mode 100644
index 0000000..4cf6a26
--- /dev/null
+++ b/src/database/migrations/1715028537217-CreateUser.ts
@@ -0,0 +1,79 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateUser1715028537217 implements MigrationInterface {
+ name = 'CreateUser1715028537217';
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ `CREATE TABLE "role" ("id" integer NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_b36bcfe02fc8de3c57a8b2391c2" PRIMARY KEY ("id"))`,
+ );
+ await queryRunner.query(
+ `CREATE TABLE "status" ("id" integer NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_e12743a7086ec826733f54e1d95" PRIMARY KEY ("id"))`,
+ );
+ await queryRunner.query(
+ `CREATE TABLE "file" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "path" character varying NOT NULL, CONSTRAINT "PK_36b46d232307066b3a2c9ea3a1d" PRIMARY KEY ("id"))`,
+ );
+ await queryRunner.query(
+ `CREATE TABLE "user" ("id" SERIAL NOT NULL, "email" character varying, "password" character varying, "provider" character varying NOT NULL DEFAULT 'email', "socialId" character varying, "firstName" character varying, "lastName" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP, "photoId" uuid, "roleId" integer, "statusId" integer, CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "REL_75e2be4ce11d447ef43be0e374" UNIQUE ("photoId"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`,
+ );
+ await queryRunner.query(
+ `CREATE INDEX "IDX_9bd2fe7a8e694dedc4ec2f666f" ON "user" ("socialId") `,
+ );
+ await queryRunner.query(
+ `CREATE INDEX "IDX_58e4dbff0e1a32a9bdc861bb29" ON "user" ("firstName") `,
+ );
+ await queryRunner.query(
+ `CREATE INDEX "IDX_f0e1b4ecdca13b177e2e3a0613" ON "user" ("lastName") `,
+ );
+ await queryRunner.query(
+ `CREATE TABLE "session" ("id" SERIAL NOT NULL, "hash" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP, "userId" integer, CONSTRAINT "PK_f55da76ac1c3ac420f444d2ff11" PRIMARY KEY ("id"))`,
+ );
+ await queryRunner.query(
+ `CREATE INDEX "IDX_3d2f174ef04fb312fdebd0ddc5" ON "session" ("userId") `,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "user" ADD CONSTRAINT "FK_75e2be4ce11d447ef43be0e374f" FOREIGN KEY ("photoId") REFERENCES "file"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "user" ADD CONSTRAINT "FK_c28e52f758e7bbc53828db92194" FOREIGN KEY ("roleId") REFERENCES "role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "user" ADD CONSTRAINT "FK_dc18daa696860586ba4667a9d31" FOREIGN KEY ("statusId") REFERENCES "status"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "session" ADD CONSTRAINT "FK_3d2f174ef04fb312fdebd0ddc53" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
+ );
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ `ALTER TABLE "session" DROP CONSTRAINT "FK_3d2f174ef04fb312fdebd0ddc53"`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "user" DROP CONSTRAINT "FK_dc18daa696860586ba4667a9d31"`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "user" DROP CONSTRAINT "FK_c28e52f758e7bbc53828db92194"`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE "user" DROP CONSTRAINT "FK_75e2be4ce11d447ef43be0e374f"`,
+ );
+ await queryRunner.query(
+ `DROP INDEX "public"."IDX_3d2f174ef04fb312fdebd0ddc5"`,
+ );
+ await queryRunner.query(`DROP TABLE "session"`);
+ await queryRunner.query(
+ `DROP INDEX "public"."IDX_f0e1b4ecdca13b177e2e3a0613"`,
+ );
+ await queryRunner.query(
+ `DROP INDEX "public"."IDX_58e4dbff0e1a32a9bdc861bb29"`,
+ );
+ await queryRunner.query(
+ `DROP INDEX "public"."IDX_9bd2fe7a8e694dedc4ec2f666f"`,
+ );
+ await queryRunner.query(`DROP TABLE "user"`);
+ await queryRunner.query(`DROP TABLE "file"`);
+ await queryRunner.query(`DROP TABLE "status"`);
+ await queryRunner.query(`DROP TABLE "role"`);
+ }
+}
diff --git a/src/database/mongoose-config.service.ts b/src/database/mongoose-config.service.ts
new file mode 100644
index 0000000..66d15b6
--- /dev/null
+++ b/src/database/mongoose-config.service.ts
@@ -0,0 +1,21 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import {
+ MongooseModuleOptions,
+ MongooseOptionsFactory,
+} from '@nestjs/mongoose';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class MongooseConfigService implements MongooseOptionsFactory {
+ constructor(private configService: ConfigService) {}
+
+ createMongooseOptions(): MongooseModuleOptions {
+ return {
+ uri: this.configService.get('database.url', { infer: true }),
+ dbName: this.configService.get('database.name', { infer: true }),
+ user: this.configService.get('database.username', { infer: true }),
+ pass: this.configService.get('database.password', { infer: true }),
+ };
+ }
+}
diff --git a/src/database/seeds/document/run-seed.ts b/src/database/seeds/document/run-seed.ts
new file mode 100644
index 0000000..9e43fbd
--- /dev/null
+++ b/src/database/seeds/document/run-seed.ts
@@ -0,0 +1,15 @@
+import { NestFactory } from '@nestjs/core';
+import { UserSeedService } from './user/user-seed.service';
+
+import { SeedModule } from './seed.module';
+
+const runSeed = async () => {
+ const app = await NestFactory.create(SeedModule);
+
+ // run
+ await app.get(UserSeedService).run();
+
+ await app.close();
+};
+
+void runSeed();
diff --git a/src/database/seeds/document/seed.module.ts b/src/database/seeds/document/seed.module.ts
new file mode 100644
index 0000000..6d24061
--- /dev/null
+++ b/src/database/seeds/document/seed.module.ts
@@ -0,0 +1,24 @@
+import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
+
+import { MongooseModule } from '@nestjs/mongoose';
+
+import { UserSeedModule } from './user/user-seed.module';
+import appConfig from '../../../config/app.config';
+import databaseConfig from '../../config/database.config';
+import { MongooseConfigService } from '../../mongoose-config.service';
+
+@Module({
+ imports: [
+ UserSeedModule,
+ ConfigModule.forRoot({
+ isGlobal: true,
+ load: [databaseConfig, appConfig],
+ envFilePath: ['.env'],
+ }),
+ MongooseModule.forRootAsync({
+ useClass: MongooseConfigService,
+ }),
+ ],
+})
+export class SeedModule {}
diff --git a/src/database/seeds/document/user/user-seed.module.ts b/src/database/seeds/document/user/user-seed.module.ts
new file mode 100644
index 0000000..de629e8
--- /dev/null
+++ b/src/database/seeds/document/user/user-seed.module.ts
@@ -0,0 +1,21 @@
+import { Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { UserSeedService } from './user-seed.service';
+import {
+ UserSchemaClass,
+ UserSchema,
+} from '../../../../users/infrastructure/persistence/document/entities/user.schema';
+
+@Module({
+ imports: [
+ MongooseModule.forFeature([
+ {
+ name: UserSchemaClass.name,
+ schema: UserSchema,
+ },
+ ]),
+ ],
+ providers: [UserSeedService],
+ exports: [UserSeedService],
+})
+export class UserSeedModule {}
diff --git a/src/database/seeds/document/user/user-seed.service.ts b/src/database/seeds/document/user/user-seed.service.ts
new file mode 100644
index 0000000..fa36217
--- /dev/null
+++ b/src/database/seeds/document/user/user-seed.service.ts
@@ -0,0 +1,64 @@
+import { Injectable } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import bcrypt from 'bcryptjs';
+import { Model } from 'mongoose';
+import { RoleEnum } from '../../../../roles/roles.enum';
+import { StatusEnum } from '../../../../statuses/statuses.enum';
+import { UserSchemaClass } from '../../../../users/infrastructure/persistence/document/entities/user.schema';
+
+@Injectable()
+export class UserSeedService {
+ constructor(
+ @InjectModel(UserSchemaClass.name)
+ private readonly model: Model,
+ ) {}
+
+ async run() {
+ const admin = await this.model.findOne({
+ email: 'admin@example.com',
+ });
+
+ if (!admin) {
+ const salt = await bcrypt.genSalt();
+ const password = await bcrypt.hash('secret', salt);
+
+ const data = new this.model({
+ email: 'admin@example.com',
+ password: password,
+ firstName: 'Super',
+ lastName: 'Admin',
+ role: {
+ _id: RoleEnum.admin.toString(),
+ },
+ status: {
+ _id: StatusEnum.active.toString(),
+ },
+ });
+ await data.save();
+ }
+
+ const user = await this.model.findOne({
+ email: 'john.doe@example.com',
+ });
+
+ if (!user) {
+ const salt = await bcrypt.genSalt();
+ const password = await bcrypt.hash('secret', salt);
+
+ const data = new this.model({
+ email: 'john.doe@example.com',
+ password: password,
+ firstName: 'John',
+ lastName: 'Doe',
+ role: {
+ _id: RoleEnum.user.toString(),
+ },
+ status: {
+ _id: StatusEnum.active.toString(),
+ },
+ });
+
+ await data.save();
+ }
+ }
+}
diff --git a/src/database/seeds/relational/role/role-seed.module.ts b/src/database/seeds/relational/role/role-seed.module.ts
new file mode 100644
index 0000000..9772758
--- /dev/null
+++ b/src/database/seeds/relational/role/role-seed.module.ts
@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+
+import { RoleSeedService } from './role-seed.service';
+import { RoleEntity } from '../../../../roles/infrastructure/persistence/relational/entities/role.entity';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([RoleEntity])],
+ providers: [RoleSeedService],
+ exports: [RoleSeedService],
+})
+export class RoleSeedModule {}
diff --git a/src/database/seeds/relational/role/role-seed.service.ts b/src/database/seeds/relational/role/role-seed.service.ts
new file mode 100644
index 0000000..ff6b256
--- /dev/null
+++ b/src/database/seeds/relational/role/role-seed.service.ts
@@ -0,0 +1,45 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+import { RoleEntity } from '../../../../roles/infrastructure/persistence/relational/entities/role.entity';
+import { RoleEnum } from '../../../../roles/roles.enum';
+
+@Injectable()
+export class RoleSeedService {
+ constructor(
+ @InjectRepository(RoleEntity)
+ private repository: Repository,
+ ) {}
+
+ async run() {
+ const countUser = await this.repository.count({
+ where: {
+ id: RoleEnum.user,
+ },
+ });
+
+ if (!countUser) {
+ await this.repository.save(
+ this.repository.create({
+ id: RoleEnum.user,
+ name: 'User',
+ }),
+ );
+ }
+
+ const countAdmin = await this.repository.count({
+ where: {
+ id: RoleEnum.admin,
+ },
+ });
+
+ if (!countAdmin) {
+ await this.repository.save(
+ this.repository.create({
+ id: RoleEnum.admin,
+ name: 'Admin',
+ }),
+ );
+ }
+ }
+}
diff --git a/src/database/seeds/relational/run-seed.ts b/src/database/seeds/relational/run-seed.ts
new file mode 100644
index 0000000..e086012
--- /dev/null
+++ b/src/database/seeds/relational/run-seed.ts
@@ -0,0 +1,18 @@
+import { NestFactory } from '@nestjs/core';
+import { RoleSeedService } from './role/role-seed.service';
+import { SeedModule } from './seed.module';
+import { StatusSeedService } from './status/status-seed.service';
+import { UserSeedService } from './user/user-seed.service';
+
+const runSeed = async () => {
+ const app = await NestFactory.create(SeedModule);
+
+ // run
+ await app.get(RoleSeedService).run();
+ await app.get(StatusSeedService).run();
+ await app.get(UserSeedService).run();
+
+ await app.close();
+};
+
+void runSeed();
diff --git a/src/database/seeds/relational/seed.module.ts b/src/database/seeds/relational/seed.module.ts
new file mode 100644
index 0000000..3951c70
--- /dev/null
+++ b/src/database/seeds/relational/seed.module.ts
@@ -0,0 +1,31 @@
+import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
+import { TypeOrmModule } from '@nestjs/typeorm';
+
+import { DataSource, DataSourceOptions } from 'typeorm';
+import { TypeOrmConfigService } from '../../typeorm-config.service';
+import { RoleSeedModule } from './role/role-seed.module';
+import { StatusSeedModule } from './status/status-seed.module';
+import { UserSeedModule } from './user/user-seed.module';
+import databaseConfig from '../../config/database.config';
+import appConfig from '../../../config/app.config';
+
+@Module({
+ imports: [
+ RoleSeedModule,
+ StatusSeedModule,
+ UserSeedModule,
+ ConfigModule.forRoot({
+ isGlobal: true,
+ load: [databaseConfig, appConfig],
+ envFilePath: ['.env'],
+ }),
+ TypeOrmModule.forRootAsync({
+ useClass: TypeOrmConfigService,
+ dataSourceFactory: async (options: DataSourceOptions) => {
+ return new DataSource(options).initialize();
+ },
+ }),
+ ],
+})
+export class SeedModule {}
diff --git a/src/database/seeds/relational/status/status-seed.module.ts b/src/database/seeds/relational/status/status-seed.module.ts
new file mode 100644
index 0000000..46b13a8
--- /dev/null
+++ b/src/database/seeds/relational/status/status-seed.module.ts
@@ -0,0 +1,11 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { StatusSeedService } from './status-seed.service';
+import { StatusEntity } from '../../../../statuses/infrastructure/persistence/relational/entities/status.entity';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([StatusEntity])],
+ providers: [StatusSeedService],
+ exports: [StatusSeedService],
+})
+export class StatusSeedModule {}
diff --git a/src/database/seeds/relational/status/status-seed.service.ts b/src/database/seeds/relational/status/status-seed.service.ts
new file mode 100644
index 0000000..83f948d
--- /dev/null
+++ b/src/database/seeds/relational/status/status-seed.service.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+import { StatusEntity } from '../../../../statuses/infrastructure/persistence/relational/entities/status.entity';
+import { StatusEnum } from '../../../../statuses/statuses.enum';
+
+@Injectable()
+export class StatusSeedService {
+ constructor(
+ @InjectRepository(StatusEntity)
+ private repository: Repository,
+ ) {}
+
+ async run() {
+ const count = await this.repository.count();
+
+ if (!count) {
+ await this.repository.save([
+ this.repository.create({
+ id: StatusEnum.active,
+ name: 'Active',
+ }),
+ this.repository.create({
+ id: StatusEnum.inactive,
+ name: 'Inactive',
+ }),
+ ]);
+ }
+ }
+}
diff --git a/src/database/seeds/relational/user/user-seed.module.ts b/src/database/seeds/relational/user/user-seed.module.ts
new file mode 100644
index 0000000..d3d7b11
--- /dev/null
+++ b/src/database/seeds/relational/user/user-seed.module.ts
@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+
+import { UserSeedService } from './user-seed.service';
+import { UserEntity } from '../../../../users/infrastructure/persistence/relational/entities/user.entity';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([UserEntity])],
+ providers: [UserSeedService],
+ exports: [UserSeedService],
+})
+export class UserSeedModule {}
diff --git a/src/database/seeds/relational/user/user-seed.service.ts b/src/database/seeds/relational/user/user-seed.service.ts
new file mode 100644
index 0000000..7f6bf65
--- /dev/null
+++ b/src/database/seeds/relational/user/user-seed.service.ts
@@ -0,0 +1,78 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+
+import { Repository } from 'typeorm';
+import bcrypt from 'bcryptjs';
+import { RoleEnum } from '../../../../roles/roles.enum';
+import { StatusEnum } from '../../../../statuses/statuses.enum';
+import { UserEntity } from '../../../../users/infrastructure/persistence/relational/entities/user.entity';
+
+@Injectable()
+export class UserSeedService {
+ constructor(
+ @InjectRepository(UserEntity)
+ private repository: Repository,
+ ) {}
+
+ async run() {
+ const countAdmin = await this.repository.count({
+ where: {
+ role: {
+ id: RoleEnum.admin,
+ },
+ },
+ });
+
+ if (!countAdmin) {
+ const salt = await bcrypt.genSalt();
+ const password = await bcrypt.hash('secret', salt);
+
+ await this.repository.save(
+ this.repository.create({
+ firstName: 'Super',
+ lastName: 'Admin',
+ email: 'admin@example.com',
+ password,
+ role: {
+ id: RoleEnum.admin,
+ name: 'Admin',
+ },
+ status: {
+ id: StatusEnum.active,
+ name: 'Active',
+ },
+ }),
+ );
+ }
+
+ const countUser = await this.repository.count({
+ where: {
+ role: {
+ id: RoleEnum.user,
+ },
+ },
+ });
+
+ if (!countUser) {
+ const salt = await bcrypt.genSalt();
+ const password = await bcrypt.hash('secret', salt);
+
+ await this.repository.save(
+ this.repository.create({
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john.doe@example.com',
+ password,
+ role: {
+ id: RoleEnum.user,
+ name: 'Admin',
+ },
+ status: {
+ id: StatusEnum.active,
+ name: 'Active',
+ },
+ }),
+ );
+ }
+ }
+}
diff --git a/src/database/typeorm-config.service.ts b/src/database/typeorm-config.service.ts
new file mode 100644
index 0000000..45c79bc
--- /dev/null
+++ b/src/database/typeorm-config.service.ts
@@ -0,0 +1,57 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class TypeOrmConfigService implements TypeOrmOptionsFactory {
+ constructor(private configService: ConfigService) {}
+
+ createTypeOrmOptions(): TypeOrmModuleOptions {
+ return {
+ type: this.configService.get('database.type', { infer: true }),
+ url: this.configService.get('database.url', { infer: true }),
+ host: this.configService.get('database.host', { infer: true }),
+ port: this.configService.get('database.port', { infer: true }),
+ username: this.configService.get('database.username', { infer: true }),
+ password: this.configService.get('database.password', { infer: true }),
+ database: this.configService.get('database.name', { infer: true }),
+ synchronize: this.configService.get('database.synchronize', {
+ infer: true,
+ }),
+ dropSchema: false,
+ keepConnectionAlive: true,
+ logging:
+ this.configService.get('app.nodeEnv', { infer: true }) !== 'production',
+ entities: [__dirname + '/../**/*.entity{.ts,.js}'],
+ migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
+ cli: {
+ entitiesDir: 'src',
+
+ subscribersDir: 'subscriber',
+ },
+ extra: {
+ // based on https://node-postgres.com/apis/pool
+ // max connection pool size
+ max: this.configService.get('database.maxConnections', { infer: true }),
+ ssl: this.configService.get('database.sslEnabled', { infer: true })
+ ? {
+ rejectUnauthorized: this.configService.get(
+ 'database.rejectUnauthorized',
+ { infer: true },
+ ),
+ ca:
+ this.configService.get('database.ca', { infer: true }) ??
+ undefined,
+ key:
+ this.configService.get('database.key', { infer: true }) ??
+ undefined,
+ cert:
+ this.configService.get('database.cert', { infer: true }) ??
+ undefined,
+ }
+ : undefined,
+ },
+ } as TypeOrmModuleOptions;
+ }
+}
diff --git a/src/files/config/file-config.type.ts b/src/files/config/file-config.type.ts
new file mode 100644
index 0000000..c0b29c9
--- /dev/null
+++ b/src/files/config/file-config.type.ts
@@ -0,0 +1,14 @@
+export enum FileDriver {
+ LOCAL = 'local',
+ S3 = 's3',
+ S3_PRESIGNED = 's3-presigned',
+}
+
+export type FileConfig = {
+ driver: FileDriver;
+ accessKeyId?: string;
+ secretAccessKey?: string;
+ awsDefaultS3Bucket?: string;
+ awsS3Region?: string;
+ maxFileSize: number;
+};
diff --git a/src/files/config/file.config.ts b/src/files/config/file.config.ts
new file mode 100644
index 0000000..422c3ab
--- /dev/null
+++ b/src/files/config/file.config.ts
@@ -0,0 +1,48 @@
+import { registerAs } from '@nestjs/config';
+
+import { IsEnum, IsString, ValidateIf } from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { FileDriver, FileConfig } from './file-config.type';
+
+class EnvironmentVariablesValidator {
+ @IsEnum(FileDriver)
+ FILE_DRIVER: FileDriver;
+
+ @ValidateIf((envValues) =>
+ [FileDriver.S3, FileDriver.S3_PRESIGNED].includes(envValues.FILE_DRIVER),
+ )
+ @IsString()
+ ACCESS_KEY_ID: string;
+
+ @ValidateIf((envValues) =>
+ [FileDriver.S3, FileDriver.S3_PRESIGNED].includes(envValues.FILE_DRIVER),
+ )
+ @IsString()
+ SECRET_ACCESS_KEY: string;
+
+ @ValidateIf((envValues) =>
+ [FileDriver.S3, FileDriver.S3_PRESIGNED].includes(envValues.FILE_DRIVER),
+ )
+ @IsString()
+ AWS_DEFAULT_S3_BUCKET: string;
+
+ @ValidateIf((envValues) =>
+ [FileDriver.S3, FileDriver.S3_PRESIGNED].includes(envValues.FILE_DRIVER),
+ )
+ @IsString()
+ AWS_S3_REGION: string;
+}
+
+export default registerAs('file', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ driver:
+ (process.env.FILE_DRIVER as FileDriver | undefined) ?? FileDriver.LOCAL,
+ accessKeyId: process.env.ACCESS_KEY_ID,
+ secretAccessKey: process.env.SECRET_ACCESS_KEY,
+ awsDefaultS3Bucket: process.env.AWS_DEFAULT_S3_BUCKET,
+ awsS3Region: process.env.AWS_S3_REGION,
+ maxFileSize: 5242880, // 5mb
+ };
+});
diff --git a/src/files/domain/file.ts b/src/files/domain/file.ts
new file mode 100644
index 0000000..537bda1
--- /dev/null
+++ b/src/files/domain/file.ts
@@ -0,0 +1,56 @@
+import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger';
+import { Allow } from 'class-validator';
+import { Transform } from 'class-transformer';
+import fileConfig from '../config/file.config';
+import { FileConfig, FileDriver } from '../config/file-config.type';
+
+import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
+import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
+import { AppConfig } from '../../config/app-config.type';
+import appConfig from '../../config/app.config';
+
+export class FileType {
+ @ApiProperty({
+ type: String,
+ example: 'cbcfa8b8-3a25-4adb-a9c6-e325f0d0f3ae',
+ })
+ @Allow()
+ id: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'https://example.com/path/to/file.jpg',
+ })
+ @Transform(
+ ({ value }) => {
+ if ((fileConfig() as FileConfig).driver === FileDriver.LOCAL) {
+ return (appConfig() as AppConfig).backendDomain + value;
+ } else if (
+ [FileDriver.S3_PRESIGNED, FileDriver.S3].includes(
+ (fileConfig() as FileConfig).driver,
+ )
+ ) {
+ const s3 = new S3Client({
+ region: (fileConfig() as FileConfig).awsS3Region ?? '',
+ credentials: {
+ accessKeyId: (fileConfig() as FileConfig).accessKeyId ?? '',
+ secretAccessKey: (fileConfig() as FileConfig).secretAccessKey ?? '',
+ },
+ });
+
+ const command = new GetObjectCommand({
+ Bucket: (fileConfig() as FileConfig).awsDefaultS3Bucket ?? '',
+ Key: value,
+ });
+
+ return getSignedUrl(s3, command, { expiresIn: 3600 });
+ }
+
+ return value;
+ },
+ {
+ toPlainOnly: true,
+ },
+ )
+ path: string;
+}
diff --git a/src/files/dto/file.dto.ts b/src/files/dto/file.dto.ts
new file mode 100644
index 0000000..fae0f9a
--- /dev/null
+++ b/src/files/dto/file.dto.ts
@@ -0,0 +1,11 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { FileType } from '../domain/file';
+import { IsString } from 'class-validator';
+
+export class FileDto implements FileType {
+ @ApiProperty()
+ @IsString()
+ id: string;
+
+ path: string;
+}
diff --git a/src/files/files.module.ts b/src/files/files.module.ts
new file mode 100644
index 0000000..16fcbcf
--- /dev/null
+++ b/src/files/files.module.ts
@@ -0,0 +1,33 @@
+import { Module } from '@nestjs/common';
+
+import { DocumentFilePersistenceModule } from './infrastructure/persistence/document/document-persistence.module';
+import { RelationalFilePersistenceModule } from './infrastructure/persistence/relational/relational-persistence.module';
+import { FilesService } from './files.service';
+import fileConfig from './config/file.config';
+import { FileConfig, FileDriver } from './config/file-config.type';
+import { FilesLocalModule } from './infrastructure/uploader/local/files.module';
+import { FilesS3Module } from './infrastructure/uploader/s3/files.module';
+import { FilesS3PresignedModule } from './infrastructure/uploader/s3-presigned/files.module';
+import { DatabaseConfig } from '../database/config/database-config.type';
+import databaseConfig from '../database/config/database.config';
+
+//
+const infrastructurePersistenceModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? DocumentFilePersistenceModule
+ : RelationalFilePersistenceModule;
+//
+
+const infrastructureUploaderModule =
+ (fileConfig() as FileConfig).driver === FileDriver.LOCAL
+ ? FilesLocalModule
+ : (fileConfig() as FileConfig).driver === FileDriver.S3
+ ? FilesS3Module
+ : FilesS3PresignedModule;
+
+@Module({
+ imports: [infrastructurePersistenceModule, infrastructureUploaderModule],
+ providers: [FilesService],
+ exports: [FilesService, infrastructurePersistenceModule],
+})
+export class FilesModule {}
diff --git a/src/files/files.service.ts b/src/files/files.service.ts
new file mode 100644
index 0000000..066b625
--- /dev/null
+++ b/src/files/files.service.ts
@@ -0,0 +1,15 @@
+import { Injectable } from '@nestjs/common';
+
+import { FileRepository } from './infrastructure/persistence/file.repository';
+import { FileType } from './domain/file';
+import { EntityCondition } from '../utils/types/entity-condition.type';
+import { NullableType } from '../utils/types/nullable.type';
+
+@Injectable()
+export class FilesService {
+ constructor(private readonly fileRepository: FileRepository) {}
+
+ findOne(fields: EntityCondition): Promise> {
+ return this.fileRepository.findOne(fields);
+ }
+}
diff --git a/src/files/infrastructure/persistence/document/document-persistence.module.ts b/src/files/infrastructure/persistence/document/document-persistence.module.ts
new file mode 100644
index 0000000..955eba3
--- /dev/null
+++ b/src/files/infrastructure/persistence/document/document-persistence.module.ts
@@ -0,0 +1,21 @@
+import { Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { FileSchema, FileSchemaClass } from './entities/file.schema';
+import { FileRepository } from '../file.repository';
+import { FileDocumentRepository } from './repositories/file.repository';
+
+@Module({
+ imports: [
+ MongooseModule.forFeature([
+ { name: FileSchemaClass.name, schema: FileSchema },
+ ]),
+ ],
+ providers: [
+ {
+ provide: FileRepository,
+ useClass: FileDocumentRepository,
+ },
+ ],
+ exports: [FileRepository],
+})
+export class DocumentFilePersistenceModule {}
diff --git a/src/files/infrastructure/persistence/document/entities/file.schema.ts b/src/files/infrastructure/persistence/document/entities/file.schema.ts
new file mode 100644
index 0000000..ea304b2
--- /dev/null
+++ b/src/files/infrastructure/persistence/document/entities/file.schema.ts
@@ -0,0 +1,64 @@
+import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
+import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+// We use class-transformer in schema and domain entity.
+// We duplicate these rules because you can choose not to use adapters
+// in your project and return an schema entity directly in response.
+import { Transform } from 'class-transformer';
+import { HydratedDocument } from 'mongoose';
+import { AppConfig } from '../../../../../config/app-config.type';
+import appConfig from '../../../../../config/app.config';
+import { EntityDocumentHelper } from '../../../../../utils/document-entity-helper';
+import { FileConfig, FileDriver } from '../../../../config/file-config.type';
+import fileConfig from '../../../../config/file.config';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+export type FileSchemaDocument = HydratedDocument;
+
+@Schema({
+ toJSON: {
+ virtuals: true,
+ getters: true,
+ },
+})
+export class FileSchemaClass extends EntityDocumentHelper {
+ @ApiResponseProperty({
+ type: String,
+ example: 'https://example.com/path/to/file.jpg',
+ })
+ @Prop()
+ @Transform(
+ ({ value }) => {
+ if ((fileConfig() as FileConfig).driver === FileDriver.LOCAL) {
+ return (appConfig() as AppConfig).backendDomain + value;
+ } else if (
+ [FileDriver.S3_PRESIGNED, FileDriver.S3].includes(
+ (fileConfig() as FileConfig).driver,
+ )
+ ) {
+ const s3 = new S3Client({
+ region: (fileConfig() as FileConfig).awsS3Region ?? '',
+ credentials: {
+ accessKeyId: (fileConfig() as FileConfig).accessKeyId ?? '',
+ secretAccessKey: (fileConfig() as FileConfig).secretAccessKey ?? '',
+ },
+ });
+
+ const command = new GetObjectCommand({
+ Bucket: (fileConfig() as FileConfig).awsDefaultS3Bucket ?? '',
+ Key: value,
+ });
+
+ return getSignedUrl(s3, command, { expiresIn: 3600 });
+ }
+
+ return value;
+ },
+ {
+ toPlainOnly: true,
+ },
+ )
+ path: string;
+}
+
+export const FileSchema = SchemaFactory.createForClass(FileSchemaClass);
diff --git a/src/files/infrastructure/persistence/document/mappers/file.mapper.ts b/src/files/infrastructure/persistence/document/mappers/file.mapper.ts
new file mode 100644
index 0000000..912df69
--- /dev/null
+++ b/src/files/infrastructure/persistence/document/mappers/file.mapper.ts
@@ -0,0 +1,19 @@
+import { FileType } from '../../../../domain/file';
+import { FileSchemaClass } from '../entities/file.schema';
+
+export class FileMapper {
+ static toDomain(raw: FileSchemaClass): FileType {
+ const file = new FileType();
+ file.id = raw._id.toString();
+ file.path = raw.path;
+ return file;
+ }
+ static toPersistence(file) {
+ const fileEntity = new FileSchemaClass();
+ if (file.id) {
+ fileEntity._id = file.id;
+ }
+ fileEntity.path = file.path;
+ return fileEntity;
+ }
+}
diff --git a/src/files/infrastructure/persistence/document/repositories/file.repository.ts b/src/files/infrastructure/persistence/document/repositories/file.repository.ts
new file mode 100644
index 0000000..63054f5
--- /dev/null
+++ b/src/files/infrastructure/persistence/document/repositories/file.repository.ts
@@ -0,0 +1,35 @@
+import { Injectable } from '@nestjs/common';
+
+import { FileRepository } from '../../file.repository';
+import { FileSchemaClass } from '../entities/file.schema';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model } from 'mongoose';
+import { FileType } from '../../../../domain/file';
+
+import { FileMapper } from '../mappers/file.mapper';
+import { EntityCondition } from '../../../../../utils/types/entity-condition.type';
+import { NullableType } from '../../../../../utils/types/nullable.type';
+import domainToDocumentCondition from '../../../../../utils/domain-to-document-condition';
+
+@Injectable()
+export class FileDocumentRepository implements FileRepository {
+ constructor(
+ @InjectModel(FileSchemaClass.name)
+ private fileModel: Model,
+ ) {}
+
+ async create(data: Omit): Promise {
+ const createdFile = new this.fileModel(data);
+ const fileObject = await createdFile.save();
+ return FileMapper.toDomain(fileObject);
+ }
+
+ async findOne(
+ fields: EntityCondition,
+ ): Promise> {
+ const fileObject = await this.fileModel.findOne(
+ domainToDocumentCondition(fields),
+ );
+ return fileObject ? FileMapper.toDomain(fileObject) : null;
+ }
+}
diff --git a/src/files/infrastructure/persistence/file.repository.ts b/src/files/infrastructure/persistence/file.repository.ts
new file mode 100644
index 0000000..0ed0a8d
--- /dev/null
+++ b/src/files/infrastructure/persistence/file.repository.ts
@@ -0,0 +1,11 @@
+import { EntityCondition } from '../../../utils/types/entity-condition.type';
+import { NullableType } from '../../../utils/types/nullable.type';
+import { FileType } from '../../domain/file';
+
+export abstract class FileRepository {
+ abstract create(data: Omit): Promise;
+
+ abstract findOne(
+ fields: EntityCondition,
+ ): Promise>;
+}
diff --git a/src/files/infrastructure/persistence/relational/entities/file.entity.ts b/src/files/infrastructure/persistence/relational/entities/file.entity.ts
new file mode 100644
index 0000000..ad60d30
--- /dev/null
+++ b/src/files/infrastructure/persistence/relational/entities/file.entity.ts
@@ -0,0 +1,61 @@
+import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
+// We use class-transformer in ORM entity and domain entity.
+// We duplicate these rules because you can choose not to use adapters
+// in your project and return an ORM entity directly in response.
+import { Transform } from 'class-transformer';
+import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
+import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
+import { AppConfig } from '../../../../../config/app-config.type';
+import appConfig from '../../../../../config/app.config';
+import { EntityRelationalHelper } from '../../../../../utils/relational-entity-helper';
+import { FileConfig, FileDriver } from '../../../../config/file-config.type';
+import fileConfig from '../../../../config/file.config';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+@Entity({ name: 'file' })
+export class FileEntity extends EntityRelationalHelper {
+ @ApiResponseProperty({
+ type: String,
+ example: 'cbcfa8b8-3a25-4adb-a9c6-e325f0d0f3ae',
+ })
+ @PrimaryGeneratedColumn('uuid')
+ id: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'https://example.com/path/to/file.jpg',
+ })
+ @Column()
+ @Transform(
+ ({ value }) => {
+ if ((fileConfig() as FileConfig).driver === FileDriver.LOCAL) {
+ return (appConfig() as AppConfig).backendDomain + value;
+ } else if (
+ [FileDriver.S3_PRESIGNED, FileDriver.S3].includes(
+ (fileConfig() as FileConfig).driver,
+ )
+ ) {
+ const s3 = new S3Client({
+ region: (fileConfig() as FileConfig).awsS3Region ?? '',
+ credentials: {
+ accessKeyId: (fileConfig() as FileConfig).accessKeyId ?? '',
+ secretAccessKey: (fileConfig() as FileConfig).secretAccessKey ?? '',
+ },
+ });
+
+ const command = new GetObjectCommand({
+ Bucket: (fileConfig() as FileConfig).awsDefaultS3Bucket ?? '',
+ Key: value,
+ });
+
+ return getSignedUrl(s3, command, { expiresIn: 3600 });
+ }
+
+ return value;
+ },
+ {
+ toPlainOnly: true,
+ },
+ )
+ path: string;
+}
diff --git a/src/files/infrastructure/persistence/relational/mappers/file.mapper.ts b/src/files/infrastructure/persistence/relational/mappers/file.mapper.ts
new file mode 100644
index 0000000..6b69293
--- /dev/null
+++ b/src/files/infrastructure/persistence/relational/mappers/file.mapper.ts
@@ -0,0 +1,18 @@
+import { FileType } from '../../../../domain/file';
+import { FileEntity } from '../entities/file.entity';
+
+export class FileMapper {
+ static toDomain(raw: FileEntity): FileType {
+ const file = new FileType();
+ file.id = raw.id;
+ file.path = raw.path;
+ return file;
+ }
+
+ static toPersistence(file: FileType): FileEntity {
+ const fileEntity = new FileEntity();
+ fileEntity.id = file.id;
+ fileEntity.path = file.path;
+ return fileEntity;
+ }
+}
diff --git a/src/files/infrastructure/persistence/relational/relational-persistence.module.ts b/src/files/infrastructure/persistence/relational/relational-persistence.module.ts
new file mode 100644
index 0000000..00d14a1
--- /dev/null
+++ b/src/files/infrastructure/persistence/relational/relational-persistence.module.ts
@@ -0,0 +1,17 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { FileEntity } from './entities/file.entity';
+import { FileRepository } from '../file.repository';
+import { FileRelationalRepository } from './repositories/file.repository';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([FileEntity])],
+ providers: [
+ {
+ provide: FileRepository,
+ useClass: FileRelationalRepository,
+ },
+ ],
+ exports: [FileRepository],
+})
+export class RelationalFilePersistenceModule {}
diff --git a/src/files/infrastructure/persistence/relational/repositories/file.repository.ts b/src/files/infrastructure/persistence/relational/repositories/file.repository.ts
new file mode 100644
index 0000000..a2aacc9
--- /dev/null
+++ b/src/files/infrastructure/persistence/relational/repositories/file.repository.ts
@@ -0,0 +1,35 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { FileEntity } from '../entities/file.entity';
+import { FindOptionsWhere, Repository } from 'typeorm';
+import { FileRepository } from '../../file.repository';
+
+import { FileMapper } from '../mappers/file.mapper';
+import { FileType } from '../../../../domain/file';
+import { EntityCondition } from '../../../../../utils/types/entity-condition.type';
+import { NullableType } from '../../../../../utils/types/nullable.type';
+
+@Injectable()
+export class FileRelationalRepository implements FileRepository {
+ constructor(
+ @InjectRepository(FileEntity)
+ private readonly fileRepository: Repository,
+ ) {}
+
+ async create(data: FileType): Promise {
+ const persistenceModel = FileMapper.toPersistence(data);
+ return this.fileRepository.save(
+ this.fileRepository.create(persistenceModel),
+ );
+ }
+
+ async findOne(
+ fields: EntityCondition,
+ ): Promise> {
+ const entity = await this.fileRepository.findOne({
+ where: fields as FindOptionsWhere,
+ });
+
+ return entity ? FileMapper.toDomain(entity) : null;
+ }
+}
diff --git a/src/files/infrastructure/uploader/local/dto/file-response.dto.ts b/src/files/infrastructure/uploader/local/dto/file-response.dto.ts
new file mode 100644
index 0000000..2aac4dd
--- /dev/null
+++ b/src/files/infrastructure/uploader/local/dto/file-response.dto.ts
@@ -0,0 +1,9 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { FileType } from '../../../../domain/file';
+
+export class FileResponseDto {
+ @ApiResponseProperty({
+ type: () => FileType,
+ })
+ file: FileType;
+}
diff --git a/src/files/infrastructure/uploader/local/files.controller.ts b/src/files/infrastructure/uploader/local/files.controller.ts
new file mode 100644
index 0000000..5d95bd0
--- /dev/null
+++ b/src/files/infrastructure/uploader/local/files.controller.ts
@@ -0,0 +1,62 @@
+import {
+ Controller,
+ Get,
+ Param,
+ Post,
+ Response,
+ UploadedFile,
+ UseGuards,
+ UseInterceptors,
+} from '@nestjs/common';
+import { FileInterceptor } from '@nestjs/platform-express';
+import {
+ ApiBearerAuth,
+ ApiBody,
+ ApiConsumes,
+ ApiExcludeEndpoint,
+ ApiOkResponse,
+ ApiTags,
+} from '@nestjs/swagger';
+import { AuthGuard } from '@nestjs/passport';
+import { FilesLocalService } from './files.service';
+import { FileResponseDto } from './dto/file-response.dto';
+
+@ApiTags('Files')
+@Controller({
+ path: 'files',
+ version: '1',
+})
+export class FilesLocalController {
+ constructor(private readonly filesService: FilesLocalService) {}
+
+ @ApiOkResponse({
+ type: FileResponseDto,
+ })
+ @ApiBearerAuth()
+ @UseGuards(AuthGuard('jwt'))
+ @Post('upload')
+ @ApiConsumes('multipart/form-data')
+ @ApiBody({
+ schema: {
+ type: 'object',
+ properties: {
+ file: {
+ type: 'string',
+ format: 'binary',
+ },
+ },
+ },
+ })
+ @UseInterceptors(FileInterceptor('file'))
+ async uploadFile(
+ @UploadedFile() file: Express.Multer.File,
+ ): Promise {
+ return this.filesService.create(file);
+ }
+
+ @Get(':path')
+ @ApiExcludeEndpoint()
+ download(@Param('path') path, @Response() response) {
+ return response.sendFile(path, { root: './files' });
+ }
+}
diff --git a/src/files/infrastructure/uploader/local/files.module.ts b/src/files/infrastructure/uploader/local/files.module.ts
new file mode 100644
index 0000000..c94cdab
--- /dev/null
+++ b/src/files/infrastructure/uploader/local/files.module.ts
@@ -0,0 +1,73 @@
+import {
+ HttpStatus,
+ Module,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { FilesLocalController } from './files.controller';
+import { MulterModule } from '@nestjs/platform-express';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { diskStorage } from 'multer';
+import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
+
+import { FilesLocalService } from './files.service';
+
+import { DocumentFilePersistenceModule } from '../../persistence/document/document-persistence.module';
+import { RelationalFilePersistenceModule } from '../../persistence/relational/relational-persistence.module';
+import { AllConfigType } from '../../../../config/config.type';
+import { DatabaseConfig } from '../../../../database/config/database-config.type';
+import databaseConfig from '../../../../database/config/database.config';
+
+//
+const infrastructurePersistenceModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? DocumentFilePersistenceModule
+ : RelationalFilePersistenceModule;
+//
+
+@Module({
+ imports: [
+ infrastructurePersistenceModule,
+ MulterModule.registerAsync({
+ imports: [ConfigModule],
+ inject: [ConfigService],
+ useFactory: (configService: ConfigService) => {
+ return {
+ fileFilter: (request, file, callback) => {
+ if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/i)) {
+ return callback(
+ new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: `cantUploadFileType`,
+ },
+ }),
+ false,
+ );
+ }
+
+ callback(null, true);
+ },
+ storage: diskStorage({
+ destination: './files',
+ filename: (request, file, callback) => {
+ callback(
+ null,
+ `${randomStringGenerator()}.${file.originalname
+ .split('.')
+ .pop()
+ ?.toLowerCase()}`,
+ );
+ },
+ }),
+ limits: {
+ fileSize: configService.get('file.maxFileSize', { infer: true }),
+ },
+ };
+ },
+ }),
+ ],
+ controllers: [FilesLocalController],
+ providers: [ConfigModule, ConfigService, FilesLocalService],
+ exports: [FilesLocalService],
+})
+export class FilesLocalModule {}
diff --git a/src/files/infrastructure/uploader/local/files.service.ts b/src/files/infrastructure/uploader/local/files.service.ts
new file mode 100644
index 0000000..55ed26f
--- /dev/null
+++ b/src/files/infrastructure/uploader/local/files.service.ts
@@ -0,0 +1,37 @@
+import {
+ HttpStatus,
+ Injectable,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+
+import { FileRepository } from '../../persistence/file.repository';
+import { AllConfigType } from '../../../../config/config.type';
+import { FileType } from '../../../domain/file';
+
+@Injectable()
+export class FilesLocalService {
+ constructor(
+ private readonly configService: ConfigService,
+ private readonly fileRepository: FileRepository,
+ ) {}
+
+ async create(file: Express.Multer.File): Promise<{ file: FileType }> {
+ if (!file) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: 'selectFile',
+ },
+ });
+ }
+
+ return {
+ file: await this.fileRepository.create({
+ path: `/${this.configService.get('app.apiPrefix', {
+ infer: true,
+ })}/v1/${file.path}`,
+ }),
+ };
+ }
+}
diff --git a/src/files/infrastructure/uploader/s3-presigned/dto/file-response.dto.ts b/src/files/infrastructure/uploader/s3-presigned/dto/file-response.dto.ts
new file mode 100644
index 0000000..e4309df
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3-presigned/dto/file-response.dto.ts
@@ -0,0 +1,14 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { FileType } from '../../../../domain/file';
+
+export class FileResponseDto {
+ @ApiResponseProperty({
+ type: () => FileType,
+ })
+ file: FileType;
+
+ @ApiResponseProperty({
+ type: String,
+ })
+ uploadSignedUrl: string;
+}
diff --git a/src/files/infrastructure/uploader/s3-presigned/dto/file.dto.ts b/src/files/infrastructure/uploader/s3-presigned/dto/file.dto.ts
new file mode 100644
index 0000000..44fce41
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3-presigned/dto/file.dto.ts
@@ -0,0 +1,12 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNumber, IsString } from 'class-validator';
+
+export class FileUploadDto {
+ @ApiProperty({ example: 'image.jpg' })
+ @IsString()
+ fileName: string;
+
+ @ApiProperty({ example: 138723 })
+ @IsNumber()
+ fileSize: number;
+}
diff --git a/src/files/infrastructure/uploader/s3-presigned/files.controller.ts b/src/files/infrastructure/uploader/s3-presigned/files.controller.ts
new file mode 100644
index 0000000..d94feee
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3-presigned/files.controller.ts
@@ -0,0 +1,25 @@
+import { Body, Controller, Post, UseGuards } from '@nestjs/common';
+import { ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger';
+import { AuthGuard } from '@nestjs/passport';
+import { FilesS3PresignedService } from './files.service';
+import { FileUploadDto } from './dto/file.dto';
+import { FileResponseDto } from './dto/file-response.dto';
+
+@ApiTags('Files')
+@Controller({
+ path: 'files',
+ version: '1',
+})
+export class FilesS3PresignedController {
+ constructor(private readonly filesService: FilesS3PresignedService) {}
+
+ @ApiOkResponse({
+ type: FileResponseDto,
+ })
+ @ApiBearerAuth()
+ @UseGuards(AuthGuard('jwt'))
+ @Post('upload')
+ async uploadFile(@Body() file: FileUploadDto) {
+ return this.filesService.create(file);
+ }
+}
diff --git a/src/files/infrastructure/uploader/s3-presigned/files.module.ts b/src/files/infrastructure/uploader/s3-presigned/files.module.ts
new file mode 100644
index 0000000..a9b003b
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3-presigned/files.module.ts
@@ -0,0 +1,89 @@
+import {
+ HttpStatus,
+ Module,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { FilesS3PresignedController } from './files.controller';
+import { MulterModule } from '@nestjs/platform-express';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
+import { S3Client } from '@aws-sdk/client-s3';
+import multerS3 from 'multer-s3';
+
+import { FilesS3PresignedService } from './files.service';
+
+import { DocumentFilePersistenceModule } from '../../persistence/document/document-persistence.module';
+import { RelationalFilePersistenceModule } from '../../persistence/relational/relational-persistence.module';
+import { AllConfigType } from '../../../../config/config.type';
+import { DatabaseConfig } from '../../../../database/config/database-config.type';
+import databaseConfig from '../../../../database/config/database.config';
+
+//
+const infrastructurePersistenceModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? DocumentFilePersistenceModule
+ : RelationalFilePersistenceModule;
+//
+
+@Module({
+ imports: [
+ infrastructurePersistenceModule,
+ MulterModule.registerAsync({
+ imports: [ConfigModule],
+ inject: [ConfigService],
+ useFactory: (configService: ConfigService) => {
+ const s3 = new S3Client({
+ region: configService.get('file.awsS3Region', { infer: true }),
+ credentials: {
+ accessKeyId: configService.getOrThrow('file.accessKeyId', {
+ infer: true,
+ }),
+ secretAccessKey: configService.getOrThrow('file.secretAccessKey', {
+ infer: true,
+ }),
+ },
+ });
+
+ return {
+ fileFilter: (request, file, callback) => {
+ if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/i)) {
+ return callback(
+ new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: `cantUploadFileType`,
+ },
+ }),
+ false,
+ );
+ }
+
+ callback(null, true);
+ },
+ storage: multerS3({
+ s3: s3,
+ bucket: '',
+ acl: 'public-read',
+ contentType: multerS3.AUTO_CONTENT_TYPE,
+ key: (request, file, callback) => {
+ callback(
+ null,
+ `${randomStringGenerator()}.${file.originalname
+ .split('.')
+ .pop()
+ ?.toLowerCase()}`,
+ );
+ },
+ }),
+ limits: {
+ fileSize: configService.get('file.maxFileSize', { infer: true }),
+ },
+ };
+ },
+ }),
+ ],
+ controllers: [FilesS3PresignedController],
+ providers: [ConfigModule, ConfigService, FilesS3PresignedService],
+ exports: [FilesS3PresignedService],
+})
+export class FilesS3PresignedModule {}
diff --git a/src/files/infrastructure/uploader/s3-presigned/files.service.ts b/src/files/infrastructure/uploader/s3-presigned/files.service.ts
new file mode 100644
index 0000000..a6eaef9
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3-presigned/files.service.ts
@@ -0,0 +1,93 @@
+import {
+ HttpStatus,
+ Injectable,
+ PayloadTooLargeException,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { FileRepository } from '../../persistence/file.repository';
+
+import { FileUploadDto } from './dto/file.dto';
+import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
+import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
+import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
+import { ConfigService } from '@nestjs/config';
+import { FileType } from '../../../domain/file';
+
+@Injectable()
+export class FilesS3PresignedService {
+ private s3: S3Client;
+
+ constructor(
+ private readonly fileRepository: FileRepository,
+ private readonly configService: ConfigService,
+ ) {
+ this.s3 = new S3Client({
+ region: configService.get('file.awsS3Region', { infer: true }),
+ credentials: {
+ accessKeyId: configService.getOrThrow('file.accessKeyId', {
+ infer: true,
+ }),
+ secretAccessKey: configService.getOrThrow('file.secretAccessKey', {
+ infer: true,
+ }),
+ },
+ });
+ }
+
+ async create(
+ file: FileUploadDto,
+ ): Promise<{ file: FileType; uploadSignedUrl: string }> {
+ if (!file) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: 'selectFile',
+ },
+ });
+ }
+
+ if (!file.fileName.match(/\.(jpg|jpeg|png|gif)$/i)) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: `cantUploadFileType`,
+ },
+ });
+ }
+
+ if (
+ file.fileSize >
+ (this.configService.get('file.maxFileSize', {
+ infer: true,
+ }) || 0)
+ ) {
+ throw new PayloadTooLargeException({
+ statusCode: HttpStatus.PAYLOAD_TOO_LARGE,
+ error: 'Payload Too Large',
+ message: 'File too large',
+ });
+ }
+
+ const key = `${randomStringGenerator()}.${file.fileName
+ .split('.')
+ .pop()
+ ?.toLowerCase()}`;
+
+ const command = new PutObjectCommand({
+ Bucket: this.configService.getOrThrow('file.awsDefaultS3Bucket', {
+ infer: true,
+ }),
+ Key: key,
+ ContentLength: file.fileSize,
+ });
+ const signedUrl = await getSignedUrl(this.s3, command, { expiresIn: 3600 });
+ const data = await this.fileRepository.create({
+ path: key,
+ });
+
+ return {
+ file: data,
+ uploadSignedUrl: signedUrl,
+ };
+ }
+}
diff --git a/src/files/infrastructure/uploader/s3/dto/file-response.dto.ts b/src/files/infrastructure/uploader/s3/dto/file-response.dto.ts
new file mode 100644
index 0000000..2aac4dd
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3/dto/file-response.dto.ts
@@ -0,0 +1,9 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { FileType } from '../../../../domain/file';
+
+export class FileResponseDto {
+ @ApiResponseProperty({
+ type: () => FileType,
+ })
+ file: FileType;
+}
diff --git a/src/files/infrastructure/uploader/s3/files.controller.ts b/src/files/infrastructure/uploader/s3/files.controller.ts
new file mode 100644
index 0000000..1768538
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3/files.controller.ts
@@ -0,0 +1,52 @@
+import {
+ Controller,
+ Post,
+ UploadedFile,
+ UseGuards,
+ UseInterceptors,
+} from '@nestjs/common';
+import { FileInterceptor } from '@nestjs/platform-express';
+import {
+ ApiBearerAuth,
+ ApiBody,
+ ApiConsumes,
+ ApiOkResponse,
+ ApiTags,
+} from '@nestjs/swagger';
+import { AuthGuard } from '@nestjs/passport';
+import { FilesS3Service } from './files.service';
+import { FileResponseDto } from './dto/file-response.dto';
+
+@ApiTags('Files')
+@Controller({
+ path: 'files',
+ version: '1',
+})
+export class FilesS3Controller {
+ constructor(private readonly filesService: FilesS3Service) {}
+
+ @ApiOkResponse({
+ type: FileResponseDto,
+ })
+ @ApiBearerAuth()
+ @UseGuards(AuthGuard('jwt'))
+ @Post('upload')
+ @ApiConsumes('multipart/form-data')
+ @ApiBody({
+ schema: {
+ type: 'object',
+ properties: {
+ file: {
+ type: 'string',
+ format: 'binary',
+ },
+ },
+ },
+ })
+ @UseInterceptors(FileInterceptor('file'))
+ async uploadFile(
+ @UploadedFile() file: Express.MulterS3.File,
+ ): Promise {
+ return this.filesService.create(file);
+ }
+}
diff --git a/src/files/infrastructure/uploader/s3/files.module.ts b/src/files/infrastructure/uploader/s3/files.module.ts
new file mode 100644
index 0000000..3afbe0c
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3/files.module.ts
@@ -0,0 +1,90 @@
+import {
+ HttpStatus,
+ Module,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { FilesS3Controller } from './files.controller';
+import { MulterModule } from '@nestjs/platform-express';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
+import { S3Client } from '@aws-sdk/client-s3';
+import multerS3 from 'multer-s3';
+
+import { FilesS3Service } from './files.service';
+
+import { DocumentFilePersistenceModule } from '../../persistence/document/document-persistence.module';
+import { RelationalFilePersistenceModule } from '../../persistence/relational/relational-persistence.module';
+import { AllConfigType } from '../../../../config/config.type';
+import { DatabaseConfig } from '../../../../database/config/database-config.type';
+import databaseConfig from '../../../../database/config/database.config';
+
+//
+const infrastructurePersistenceModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? DocumentFilePersistenceModule
+ : RelationalFilePersistenceModule;
+//
+
+@Module({
+ imports: [
+ infrastructurePersistenceModule,
+ MulterModule.registerAsync({
+ imports: [ConfigModule],
+ inject: [ConfigService],
+ useFactory: (configService: ConfigService) => {
+ const s3 = new S3Client({
+ region: configService.get('file.awsS3Region', { infer: true }),
+ credentials: {
+ accessKeyId: configService.getOrThrow('file.accessKeyId', {
+ infer: true,
+ }),
+ secretAccessKey: configService.getOrThrow('file.secretAccessKey', {
+ infer: true,
+ }),
+ },
+ });
+
+ return {
+ fileFilter: (request, file, callback) => {
+ if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/i)) {
+ return callback(
+ new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: `cantUploadFileType`,
+ },
+ }),
+ false,
+ );
+ }
+
+ callback(null, true);
+ },
+ storage: multerS3({
+ s3: s3,
+ bucket: configService.getOrThrow('file.awsDefaultS3Bucket', {
+ infer: true,
+ }),
+ contentType: multerS3.AUTO_CONTENT_TYPE,
+ key: (request, file, callback) => {
+ callback(
+ null,
+ `${randomStringGenerator()}.${file.originalname
+ .split('.')
+ .pop()
+ ?.toLowerCase()}`,
+ );
+ },
+ }),
+ limits: {
+ fileSize: configService.get('file.maxFileSize', { infer: true }),
+ },
+ };
+ },
+ }),
+ ],
+ controllers: [FilesS3Controller],
+ providers: [FilesS3Service],
+ exports: [FilesS3Service],
+})
+export class FilesS3Module {}
diff --git a/src/files/infrastructure/uploader/s3/files.service.ts b/src/files/infrastructure/uploader/s3/files.service.ts
new file mode 100644
index 0000000..10a9123
--- /dev/null
+++ b/src/files/infrastructure/uploader/s3/files.service.ts
@@ -0,0 +1,29 @@
+import {
+ HttpStatus,
+ Injectable,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { FileRepository } from '../../persistence/file.repository';
+import { FileType } from '../../../domain/file';
+
+@Injectable()
+export class FilesS3Service {
+ constructor(private readonly fileRepository: FileRepository) {}
+
+ async create(file: Express.MulterS3.File): Promise<{ file: FileType }> {
+ if (!file) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ file: 'selectFile',
+ },
+ });
+ }
+
+ return {
+ file: await this.fileRepository.create({
+ path: file.key,
+ }),
+ };
+ }
+}
diff --git a/src/home/home.controller.ts b/src/home/home.controller.ts
new file mode 100644
index 0000000..f55dba0
--- /dev/null
+++ b/src/home/home.controller.ts
@@ -0,0 +1,15 @@
+import { Controller, Get } from '@nestjs/common';
+import { ApiTags } from '@nestjs/swagger';
+
+import { HomeService } from './home.service';
+
+@ApiTags('Home')
+@Controller()
+export class HomeController {
+ constructor(private service: HomeService) {}
+
+ @Get()
+ appInfo() {
+ return this.service.appInfo();
+ }
+}
diff --git a/src/home/home.module.ts b/src/home/home.module.ts
new file mode 100644
index 0000000..c48f86c
--- /dev/null
+++ b/src/home/home.module.ts
@@ -0,0 +1,11 @@
+import { Module } from '@nestjs/common';
+import { HomeService } from './home.service';
+import { HomeController } from './home.controller';
+import { ConfigModule } from '@nestjs/config';
+
+@Module({
+ imports: [ConfigModule],
+ controllers: [HomeController],
+ providers: [HomeService],
+})
+export class HomeModule {}
diff --git a/src/home/home.service.ts b/src/home/home.service.ts
new file mode 100644
index 0000000..1f2bb36
--- /dev/null
+++ b/src/home/home.service.ts
@@ -0,0 +1,12 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class HomeService {
+ constructor(private configService: ConfigService) {}
+
+ appInfo() {
+ return { name: this.configService.get('app.name', { infer: true }) };
+ }
+}
diff --git a/src/i18n/en/common.json b/src/i18n/en/common.json
new file mode 100644
index 0000000..b7a2419
--- /dev/null
+++ b/src/i18n/en/common.json
@@ -0,0 +1,4 @@
+{
+ "confirmEmail": "Confirm email",
+ "resetPassword": "Reset password"
+}
diff --git a/src/i18n/en/confirm-email.json b/src/i18n/en/confirm-email.json
new file mode 100644
index 0000000..0eb981d
--- /dev/null
+++ b/src/i18n/en/confirm-email.json
@@ -0,0 +1,5 @@
+{
+ "text1": "Hey!",
+ "text2": "You’re almost ready to start enjoying",
+ "text3": "Simply click the big green button below to verify your email address."
+}
diff --git a/src/i18n/en/confirm-new-email.json b/src/i18n/en/confirm-new-email.json
new file mode 100644
index 0000000..155b409
--- /dev/null
+++ b/src/i18n/en/confirm-new-email.json
@@ -0,0 +1,5 @@
+{
+ "text1": "Hey!",
+ "text2": "Confirm your new email address.",
+ "text3": "Simply click the big green button below to verify your email address."
+}
diff --git a/src/i18n/en/reset-password.json b/src/i18n/en/reset-password.json
new file mode 100644
index 0000000..6cae96a
--- /dev/null
+++ b/src/i18n/en/reset-password.json
@@ -0,0 +1,6 @@
+{
+ "text1": "Trouble signing in?",
+ "text2": "Resetting your password is easy.",
+ "text3": "Just press the button below and follow the instructions. We’ll have you up and running in no time.",
+ "text4": "If you did not make this request then please ignore this email."
+}
diff --git a/src/mail/config/mail-config.type.ts b/src/mail/config/mail-config.type.ts
new file mode 100644
index 0000000..083b844
--- /dev/null
+++ b/src/mail/config/mail-config.type.ts
@@ -0,0 +1,11 @@
+export type MailConfig = {
+ port: number;
+ host?: string;
+ user?: string;
+ password?: string;
+ defaultEmail?: string;
+ defaultName?: string;
+ ignoreTLS: boolean;
+ secure: boolean;
+ requireTLS: boolean;
+};
diff --git a/src/mail/config/mail.config.ts b/src/mail/config/mail.config.ts
new file mode 100644
index 0000000..060799b
--- /dev/null
+++ b/src/mail/config/mail.config.ts
@@ -0,0 +1,63 @@
+import { registerAs } from '@nestjs/config';
+
+import {
+ IsString,
+ IsInt,
+ Min,
+ Max,
+ IsOptional,
+ IsBoolean,
+ IsEmail,
+} from 'class-validator';
+import validateConfig from '../../utils/validate-config';
+import { MailConfig } from './mail-config.type';
+
+class EnvironmentVariablesValidator {
+ @IsInt()
+ @Min(0)
+ @Max(65535)
+ @IsOptional()
+ MAIL_PORT: number;
+
+ @IsString()
+ MAIL_HOST: string;
+
+ @IsString()
+ @IsOptional()
+ MAIL_USER: string;
+
+ @IsString()
+ @IsOptional()
+ MAIL_PASSWORD: string;
+
+ @IsEmail()
+ MAIL_DEFAULT_EMAIL: string;
+
+ @IsString()
+ MAIL_DEFAULT_NAME: string;
+
+ @IsBoolean()
+ MAIL_IGNORE_TLS: boolean;
+
+ @IsBoolean()
+ MAIL_SECURE: boolean;
+
+ @IsBoolean()
+ MAIL_REQUIRE_TLS: boolean;
+}
+
+export default registerAs('mail', () => {
+ validateConfig(process.env, EnvironmentVariablesValidator);
+
+ return {
+ port: process.env.MAIL_PORT ? parseInt(process.env.MAIL_PORT, 10) : 587,
+ host: process.env.MAIL_HOST,
+ user: process.env.MAIL_USER,
+ password: process.env.MAIL_PASSWORD,
+ defaultEmail: process.env.MAIL_DEFAULT_EMAIL,
+ defaultName: process.env.MAIL_DEFAULT_NAME,
+ ignoreTLS: process.env.MAIL_IGNORE_TLS === 'true',
+ secure: process.env.MAIL_SECURE === 'true',
+ requireTLS: process.env.MAIL_REQUIRE_TLS === 'true',
+ };
+});
diff --git a/src/mail/interfaces/mail-data.interface.ts b/src/mail/interfaces/mail-data.interface.ts
new file mode 100644
index 0000000..14e189e
--- /dev/null
+++ b/src/mail/interfaces/mail-data.interface.ts
@@ -0,0 +1,4 @@
+export interface MailData {
+ to: string;
+ data: T;
+}
diff --git a/src/mail/mail-templates/activation.hbs b/src/mail/mail-templates/activation.hbs
new file mode 100644
index 0000000..edee05e
--- /dev/null
+++ b/src/mail/mail-templates/activation.hbs
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+ {{app_name}}
+ |
+
+
+
+ {{text1}}
+ {{text2}} {{app_name}}.
+ {{text3}}
+ |
+
+
+
+ {{actionTitle}}
+ |
+
+
+
+
+
\ No newline at end of file
diff --git a/src/mail/mail-templates/confirm-new-email.hbs b/src/mail/mail-templates/confirm-new-email.hbs
new file mode 100644
index 0000000..b0c26db
--- /dev/null
+++ b/src/mail/mail-templates/confirm-new-email.hbs
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+ {{app_name}}
+ |
+
+
+
+ {{text1}}
+ {{text2}}
+ {{text3}}
+ |
+
+
+
+ {{actionTitle}}
+ |
+
+
+
+
+
\ No newline at end of file
diff --git a/src/mail/mail-templates/reset-password.hbs b/src/mail/mail-templates/reset-password.hbs
new file mode 100644
index 0000000..3c0e405
--- /dev/null
+++ b/src/mail/mail-templates/reset-password.hbs
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+ {{app_name}}
+ |
+
+
+
+ {{text1}}
+ {{text2}}
+ {{text3}}
+ |
+
+
+
+ {{actionTitle}}
+ |
+
+
+
+ {{text4}}
+ |
+
+
+
+
+
\ No newline at end of file
diff --git a/src/mail/mail.module.ts b/src/mail/mail.module.ts
new file mode 100644
index 0000000..a098cc3
--- /dev/null
+++ b/src/mail/mail.module.ts
@@ -0,0 +1,11 @@
+import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
+import { MailService } from './mail.service';
+import { MailerModule } from '../mailer/mailer.module';
+
+@Module({
+ imports: [ConfigModule, MailerModule],
+ providers: [MailService],
+ exports: [MailService],
+})
+export class MailModule {}
diff --git a/src/mail/mail.service.ts b/src/mail/mail.service.ts
new file mode 100644
index 0000000..3bdfd44
--- /dev/null
+++ b/src/mail/mail.service.ts
@@ -0,0 +1,169 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { I18nContext } from 'nestjs-i18n';
+import { MailData } from './interfaces/mail-data.interface';
+
+import { MaybeType } from '../utils/types/maybe.type';
+import { MailerService } from '../mailer/mailer.service';
+import path from 'path';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class MailService {
+ constructor(
+ private readonly mailerService: MailerService,
+ private readonly configService: ConfigService,
+ ) {}
+
+ async userSignUp(mailData: MailData<{ hash: string }>): Promise {
+ const i18n = I18nContext.current();
+ let emailConfirmTitle: MaybeType;
+ let text1: MaybeType;
+ let text2: MaybeType;
+ let text3: MaybeType;
+
+ if (i18n) {
+ [emailConfirmTitle, text1, text2, text3] = await Promise.all([
+ i18n.t('common.confirmEmail'),
+ i18n.t('confirm-email.text1'),
+ i18n.t('confirm-email.text2'),
+ i18n.t('confirm-email.text3'),
+ ]);
+ }
+
+ const url = new URL(
+ this.configService.getOrThrow('app.frontendDomain', {
+ infer: true,
+ }) + '/confirm-email',
+ );
+ url.searchParams.set('hash', mailData.data.hash);
+
+ await this.mailerService.sendMail({
+ to: mailData.to,
+ subject: emailConfirmTitle,
+ text: `${url.toString()} ${emailConfirmTitle}`,
+ templatePath: path.join(
+ this.configService.getOrThrow('app.workingDirectory', {
+ infer: true,
+ }),
+ 'src',
+ 'mail',
+ 'mail-templates',
+ 'activation.hbs',
+ ),
+ context: {
+ title: emailConfirmTitle,
+ url: url.toString(),
+ actionTitle: emailConfirmTitle,
+ app_name: this.configService.get('app.name', { infer: true }),
+ text1,
+ text2,
+ text3,
+ },
+ });
+ }
+
+ async forgotPassword(
+ mailData: MailData<{ hash: string; tokenExpires: number }>,
+ ): Promise {
+ const i18n = I18nContext.current();
+ let resetPasswordTitle: MaybeType;
+ let text1: MaybeType;
+ let text2: MaybeType;
+ let text3: MaybeType;
+ let text4: MaybeType;
+
+ if (i18n) {
+ [resetPasswordTitle, text1, text2, text3, text4] = await Promise.all([
+ i18n.t('common.resetPassword'),
+ i18n.t('reset-password.text1'),
+ i18n.t('reset-password.text2'),
+ i18n.t('reset-password.text3'),
+ i18n.t('reset-password.text4'),
+ ]);
+ }
+
+ const url = new URL(
+ this.configService.getOrThrow('app.frontendDomain', {
+ infer: true,
+ }) + '/password-change',
+ );
+ url.searchParams.set('hash', mailData.data.hash);
+ url.searchParams.set('expires', mailData.data.tokenExpires.toString());
+
+ await this.mailerService.sendMail({
+ to: mailData.to,
+ subject: resetPasswordTitle,
+ text: `${url.toString()} ${resetPasswordTitle}`,
+ templatePath: path.join(
+ this.configService.getOrThrow('app.workingDirectory', {
+ infer: true,
+ }),
+ 'src',
+ 'mail',
+ 'mail-templates',
+ 'reset-password.hbs',
+ ),
+ context: {
+ title: resetPasswordTitle,
+ url: url.toString(),
+ actionTitle: resetPasswordTitle,
+ app_name: this.configService.get('app.name', {
+ infer: true,
+ }),
+ text1,
+ text2,
+ text3,
+ text4,
+ },
+ });
+ }
+
+ async confirmNewEmail(mailData: MailData<{ hash: string }>): Promise {
+ const i18n = I18nContext.current();
+ let emailConfirmTitle: MaybeType;
+ let text1: MaybeType;
+ let text2: MaybeType;
+ let text3: MaybeType;
+
+ if (i18n) {
+ [emailConfirmTitle, text1, text2, text3] = await Promise.all([
+ i18n.t('common.confirmEmail'),
+ i18n.t('confirm-new-email.text1'),
+ i18n.t('confirm-new-email.text2'),
+ i18n.t('confirm-new-email.text3'),
+ ]);
+ }
+
+ const url = new URL(
+ this.configService.getOrThrow('app.frontendDomain', {
+ infer: true,
+ }) + '/confirm-new-email',
+ );
+ url.searchParams.set('hash', mailData.data.hash);
+
+ await this.mailerService.sendMail({
+ to: mailData.to,
+ subject: emailConfirmTitle,
+ text: `${url.toString()} ${emailConfirmTitle}`,
+ templatePath: path.join(
+ this.configService.getOrThrow('app.workingDirectory', {
+ infer: true,
+ }),
+ 'src',
+ 'mail',
+ 'mail-templates',
+ 'confirm-new-email.hbs',
+ ),
+ context: {
+ title: emailConfirmTitle,
+ url: url.toString(),
+ actionTitle: emailConfirmTitle,
+ app_name: this.configService.get('app.name', { infer: true }),
+ text1,
+ text2,
+ text3,
+ },
+ });
+ }
+}
diff --git a/src/mailer/mailer.module.ts b/src/mailer/mailer.module.ts
new file mode 100644
index 0000000..eba8e9c
--- /dev/null
+++ b/src/mailer/mailer.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { MailerService } from './mailer.service';
+
+@Module({
+ providers: [MailerService],
+ exports: [MailerService],
+})
+export class MailerModule {}
diff --git a/src/mailer/mailer.service.ts b/src/mailer/mailer.service.ts
new file mode 100644
index 0000000..ce24426
--- /dev/null
+++ b/src/mailer/mailer.service.ts
@@ -0,0 +1,53 @@
+import { Injectable } from '@nestjs/common';
+import fs from 'node:fs/promises';
+import { ConfigService } from '@nestjs/config';
+import nodemailer from 'nodemailer';
+import Handlebars from 'handlebars';
+import { AllConfigType } from '../config/config.type';
+
+@Injectable()
+export class MailerService {
+ private readonly transporter: nodemailer.Transporter;
+ constructor(private readonly configService: ConfigService) {
+ this.transporter = nodemailer.createTransport({
+ host: configService.get('mail.host', { infer: true }),
+ port: configService.get('mail.port', { infer: true }),
+ ignoreTLS: configService.get('mail.ignoreTLS', { infer: true }),
+ secure: configService.get('mail.secure', { infer: true }),
+ requireTLS: configService.get('mail.requireTLS', { infer: true }),
+ auth: {
+ user: configService.get('mail.user', { infer: true }),
+ pass: configService.get('mail.password', { infer: true }),
+ },
+ });
+ }
+
+ async sendMail({
+ templatePath,
+ context,
+ ...mailOptions
+ }: nodemailer.SendMailOptions & {
+ templatePath: string;
+ context: Record;
+ }): Promise {
+ let html: string | undefined;
+ if (templatePath) {
+ const template = await fs.readFile(templatePath, 'utf-8');
+ html = Handlebars.compile(template, {
+ strict: true,
+ })(context);
+ }
+
+ await this.transporter.sendMail({
+ ...mailOptions,
+ from: mailOptions.from
+ ? mailOptions.from
+ : `"${this.configService.get('mail.defaultName', {
+ infer: true,
+ })}" <${this.configService.get('mail.defaultEmail', {
+ infer: true,
+ })}>`,
+ html: mailOptions.html ? mailOptions.html : html,
+ });
+ }
+}
diff --git a/src/main.ts b/src/main.ts
index 13cad38..1e2998b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,8 +1,51 @@
-import { NestFactory } from '@nestjs/core';
+import 'dotenv/config';
+import {
+ ClassSerializerInterceptor,
+ ValidationPipe,
+ VersioningType,
+} from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { NestFactory, Reflector } from '@nestjs/core';
+import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
+import { useContainer } from 'class-validator';
import { AppModule } from './app.module';
+import validationOptions from './utils/validation-options';
+import { AllConfigType } from './config/config.type';
+import { ResolvePromisesInterceptor } from './utils/serializer.interceptor';
async function bootstrap() {
- const app = await NestFactory.create(AppModule);
- await app.listen(3000);
+ const app = await NestFactory.create(AppModule, { cors: true });
+ useContainer(app.select(AppModule), { fallbackOnErrors: true });
+ const configService = app.get(ConfigService);
+
+ app.enableShutdownHooks();
+ app.setGlobalPrefix(
+ configService.getOrThrow('app.apiPrefix', { infer: true }),
+ {
+ exclude: ['/'],
+ },
+ );
+ app.enableVersioning({
+ type: VersioningType.URI,
+ });
+ app.useGlobalPipes(new ValidationPipe(validationOptions));
+ app.useGlobalInterceptors(
+ // ResolvePromisesInterceptor is used to resolve promises in responses because class-transformer can't do it
+ // https://github.com/typestack/class-transformer/issues/549
+ new ResolvePromisesInterceptor(),
+ new ClassSerializerInterceptor(app.get(Reflector)),
+ );
+
+ const options = new DocumentBuilder()
+ .setTitle('API')
+ .setDescription('API docs')
+ .setVersion('1.0')
+ .addBearerAuth()
+ .build();
+
+ const document = SwaggerModule.createDocument(app, options);
+ SwaggerModule.setup('docs', app, document);
+
+ await app.listen(configService.getOrThrow('app.port', { infer: true }));
}
-bootstrap();
+void bootstrap();
diff --git a/src/roles/domain/role.ts b/src/roles/domain/role.ts
new file mode 100644
index 0000000..1270c6f
--- /dev/null
+++ b/src/roles/domain/role.ts
@@ -0,0 +1,25 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { Allow } from 'class-validator';
+import databaseConfig from '../../database/config/database.config';
+import { DatabaseConfig } from '../../database/config/database-config.type';
+
+//
+const idType = (databaseConfig() as DatabaseConfig).isDocumentDatabase
+ ? String
+ : Number;
+//
+
+export class Role {
+ @Allow()
+ @ApiResponseProperty({
+ type: idType,
+ })
+ id: number | string;
+
+ @Allow()
+ @ApiResponseProperty({
+ type: String,
+ example: 'admin',
+ })
+ name?: string;
+}
diff --git a/src/roles/dto/role.dto.ts b/src/roles/dto/role.dto.ts
new file mode 100644
index 0000000..6637e02
--- /dev/null
+++ b/src/roles/dto/role.dto.ts
@@ -0,0 +1,9 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNumber } from 'class-validator';
+import { Role } from '../domain/role';
+
+export class RoleDto implements Role {
+ @ApiProperty()
+ @IsNumber()
+ id: number;
+}
diff --git a/src/roles/infrastructure/persistence/document/entities/role.schema.ts b/src/roles/infrastructure/persistence/document/entities/role.schema.ts
new file mode 100644
index 0000000..6d8055b
--- /dev/null
+++ b/src/roles/infrastructure/persistence/document/entities/role.schema.ts
@@ -0,0 +1,14 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+export class RoleSchema {
+ @ApiResponseProperty({
+ type: String,
+ })
+ _id: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'admin',
+ })
+ name?: string;
+}
diff --git a/src/roles/infrastructure/persistence/relational/entities/role.entity.ts b/src/roles/infrastructure/persistence/relational/entities/role.entity.ts
new file mode 100644
index 0000000..5aba62d
--- /dev/null
+++ b/src/roles/infrastructure/persistence/relational/entities/role.entity.ts
@@ -0,0 +1,21 @@
+import { Column, Entity, PrimaryColumn } from 'typeorm';
+import { EntityRelationalHelper } from '../../../../../utils/relational-entity-helper';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+@Entity({
+ name: 'role',
+})
+export class RoleEntity extends EntityRelationalHelper {
+ @ApiResponseProperty({
+ type: Number,
+ })
+ @PrimaryColumn()
+ id: number;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'admin',
+ })
+ @Column()
+ name?: string;
+}
diff --git a/src/roles/roles.decorator.ts b/src/roles/roles.decorator.ts
new file mode 100644
index 0000000..da1f22f
--- /dev/null
+++ b/src/roles/roles.decorator.ts
@@ -0,0 +1,3 @@
+import { SetMetadata } from '@nestjs/common';
+
+export const Roles = (...roles: number[]) => SetMetadata('roles', roles);
diff --git a/src/roles/roles.enum.ts b/src/roles/roles.enum.ts
new file mode 100644
index 0000000..a092681
--- /dev/null
+++ b/src/roles/roles.enum.ts
@@ -0,0 +1,4 @@
+export enum RoleEnum {
+ 'admin' = 1,
+ 'user' = 2,
+}
diff --git a/src/roles/roles.guard.ts b/src/roles/roles.guard.ts
new file mode 100644
index 0000000..2b101b3
--- /dev/null
+++ b/src/roles/roles.guard.ts
@@ -0,0 +1,20 @@
+import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+
+@Injectable()
+export class RolesGuard implements CanActivate {
+ constructor(private reflector: Reflector) {}
+
+ canActivate(context: ExecutionContext): boolean {
+ const roles = this.reflector.getAllAndOverride<(number | string)[]>(
+ 'roles',
+ [context.getClass(), context.getHandler()],
+ );
+ if (!roles.length) {
+ return true;
+ }
+ const request = context.switchToHttp().getRequest();
+
+ return roles.map(String).includes(String(request.user?.role?.id));
+ }
+}
diff --git a/src/session/domain/session.ts b/src/session/domain/session.ts
new file mode 100644
index 0000000..5a531fe
--- /dev/null
+++ b/src/session/domain/session.ts
@@ -0,0 +1,10 @@
+import { User } from '../../users/domain/user';
+
+export class Session {
+ id: number | string;
+ user: User;
+ hash: string;
+ createdAt: Date;
+ updatedAt: Date;
+ deletedAt: Date;
+}
diff --git a/src/session/infrastructure/persistence/document/document-persistence.module.ts b/src/session/infrastructure/persistence/document/document-persistence.module.ts
new file mode 100644
index 0000000..0b8ca00
--- /dev/null
+++ b/src/session/infrastructure/persistence/document/document-persistence.module.ts
@@ -0,0 +1,21 @@
+import { Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { SessionSchema, SessionSchemaClass } from './entities/session.schema';
+import { SessionRepository } from '../session.repository';
+import { SessionDocumentRepository } from './repositories/session.repository';
+
+@Module({
+ imports: [
+ MongooseModule.forFeature([
+ { name: SessionSchemaClass.name, schema: SessionSchema },
+ ]),
+ ],
+ providers: [
+ {
+ provide: SessionRepository,
+ useClass: SessionDocumentRepository,
+ },
+ ],
+ exports: [SessionRepository],
+})
+export class DocumentSessionPersistenceModule {}
diff --git a/src/session/infrastructure/persistence/document/entities/session.schema.ts b/src/session/infrastructure/persistence/document/entities/session.schema.ts
new file mode 100644
index 0000000..568944a
--- /dev/null
+++ b/src/session/infrastructure/persistence/document/entities/session.schema.ts
@@ -0,0 +1,34 @@
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+import mongoose, { now, HydratedDocument } from 'mongoose';
+import { UserSchemaClass } from '../../../../../users/infrastructure/persistence/document/entities/user.schema';
+import { EntityDocumentHelper } from '../../../../../utils/document-entity-helper';
+
+export type SessionSchemaDocument = HydratedDocument;
+
+@Schema({
+ timestamps: true,
+ toJSON: {
+ virtuals: true,
+ getters: true,
+ },
+})
+export class SessionSchemaClass extends EntityDocumentHelper {
+ @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'UserSchemaClass' })
+ user: UserSchemaClass;
+
+ @Prop()
+ hash: string;
+
+ @Prop({ default: now })
+ createdAt: Date;
+
+ @Prop({ default: now })
+ updatedAt: Date;
+
+ @Prop()
+ deletedAt: Date;
+}
+
+export const SessionSchema = SchemaFactory.createForClass(SessionSchemaClass);
+
+SessionSchema.index({ user: 1 });
diff --git a/src/session/infrastructure/persistence/document/mappers/session.mapper.ts b/src/session/infrastructure/persistence/document/mappers/session.mapper.ts
new file mode 100644
index 0000000..78a32a7
--- /dev/null
+++ b/src/session/infrastructure/persistence/document/mappers/session.mapper.ts
@@ -0,0 +1,35 @@
+import { UserSchemaClass } from '../../../../../users/infrastructure/persistence/document/entities/user.schema';
+import { UserMapper } from '../../../../../users/infrastructure/persistence/document/mappers/user.mapper';
+import { Session } from '../../../../domain/session';
+import { SessionSchemaClass } from '../entities/session.schema';
+
+export class SessionMapper {
+ static toDomain(raw: SessionSchemaClass): Session {
+ const session = new Session();
+ session.id = raw._id.toString();
+
+ if (raw.user) {
+ session.user = UserMapper.toDomain(raw.user);
+ }
+
+ session.hash = raw.hash;
+ session.createdAt = raw.createdAt;
+ session.updatedAt = raw.updatedAt;
+ session.deletedAt = raw.deletedAt;
+ return session;
+ }
+ static toPersistence(session: Session): SessionSchemaClass {
+ const user = new UserSchemaClass();
+ user._id = session.user.id.toString();
+ const sessionEntity = new SessionSchemaClass();
+ if (session.id && typeof session.id === 'string') {
+ sessionEntity._id = session.id;
+ }
+ sessionEntity.user = user;
+ sessionEntity.hash = session.hash;
+ sessionEntity.createdAt = session.createdAt;
+ sessionEntity.updatedAt = session.updatedAt;
+ sessionEntity.deletedAt = session.deletedAt;
+ return sessionEntity;
+ }
+}
diff --git a/src/session/infrastructure/persistence/document/repositories/session.repository.ts b/src/session/infrastructure/persistence/document/repositories/session.repository.ts
new file mode 100644
index 0000000..f6980ef
--- /dev/null
+++ b/src/session/infrastructure/persistence/document/repositories/session.repository.ts
@@ -0,0 +1,82 @@
+import { Injectable } from '@nestjs/common';
+import { NullableType } from '../../../../../utils/types/nullable.type';
+import { SessionRepository } from '../../session.repository';
+import { Session } from '../../../../domain/session';
+import { SessionSchemaClass } from '../entities/session.schema';
+import { Model } from 'mongoose';
+import { InjectModel } from '@nestjs/mongoose';
+import { SessionMapper } from '../mappers/session.mapper';
+import { User } from '../../../../../users/domain/user';
+import { EntityCondition } from '../../../../../utils/types/entity-condition.type';
+import domainToDocumentCondition from '../../../../../utils/domain-to-document-condition';
+
+@Injectable()
+export class SessionDocumentRepository implements SessionRepository {
+ constructor(
+ @InjectModel(SessionSchemaClass.name)
+ private sessionModel: Model,
+ ) {}
+
+ async findOne(
+ fields: EntityCondition,
+ ): Promise> {
+ const sessionObject = await this.sessionModel.findOne(
+ domainToDocumentCondition(fields),
+ );
+ return sessionObject ? SessionMapper.toDomain(sessionObject) : null;
+ }
+
+ async create(data: Session): Promise {
+ const persistenceModel = SessionMapper.toPersistence(data);
+ const createdSession = new this.sessionModel(persistenceModel);
+ const sessionObject = await createdSession.save();
+ return SessionMapper.toDomain(sessionObject);
+ }
+
+ async update(
+ id: Session['id'],
+ payload: Partial,
+ ): Promise {
+ const clonedPayload = { ...payload };
+ delete clonedPayload.id;
+ delete clonedPayload.createdAt;
+ delete clonedPayload.updatedAt;
+ delete clonedPayload.deletedAt;
+
+ const filter = { _id: id.toString() };
+ const session = await this.sessionModel.findOne(filter);
+
+ if (!session) {
+ return null;
+ }
+
+ const sessionObject = await this.sessionModel.findOneAndUpdate(
+ filter,
+ SessionMapper.toPersistence({
+ ...SessionMapper.toDomain(session),
+ ...clonedPayload,
+ }),
+ );
+
+ return sessionObject ? SessionMapper.toDomain(sessionObject) : null;
+ }
+
+ async softDelete({
+ excludeId,
+ ...criteria
+ }: {
+ id?: Session['id'];
+ user?: Pick;
+ excludeId?: Session['id'];
+ }): Promise {
+ const transformedCriteria = {
+ user: criteria.user?.id,
+ _id: criteria.id
+ ? criteria.id.toString()
+ : excludeId
+ ? { $not: { $eq: excludeId.toString() } }
+ : undefined,
+ };
+ await this.sessionModel.deleteMany(transformedCriteria);
+ }
+}
diff --git a/src/session/infrastructure/persistence/relational/entities/session.entity.ts b/src/session/infrastructure/persistence/relational/entities/session.entity.ts
new file mode 100644
index 0000000..15927a5
--- /dev/null
+++ b/src/session/infrastructure/persistence/relational/entities/session.entity.ts
@@ -0,0 +1,39 @@
+import {
+ CreateDateColumn,
+ Entity,
+ Index,
+ ManyToOne,
+ PrimaryGeneratedColumn,
+ DeleteDateColumn,
+ Column,
+ UpdateDateColumn,
+} from 'typeorm';
+import { UserEntity } from '../../../../../users/infrastructure/persistence/relational/entities/user.entity';
+
+import { EntityRelationalHelper } from '../../../../../utils/relational-entity-helper';
+
+@Entity({
+ name: 'session',
+})
+export class SessionEntity extends EntityRelationalHelper {
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @ManyToOne(() => UserEntity, {
+ eager: true,
+ })
+ @Index()
+ user: UserEntity;
+
+ @Column()
+ hash: string;
+
+ @CreateDateColumn()
+ createdAt: Date;
+
+ @UpdateDateColumn()
+ updatedAt: Date;
+
+ @DeleteDateColumn()
+ deletedAt: Date;
+}
diff --git a/src/session/infrastructure/persistence/relational/mappers/session.mapper.ts b/src/session/infrastructure/persistence/relational/mappers/session.mapper.ts
new file mode 100644
index 0000000..aac1b76
--- /dev/null
+++ b/src/session/infrastructure/persistence/relational/mappers/session.mapper.ts
@@ -0,0 +1,35 @@
+import { UserEntity } from '../../../../../users/infrastructure/persistence/relational/entities/user.entity';
+import { UserMapper } from '../../../../../users/infrastructure/persistence/relational/mappers/user.mapper';
+import { Session } from '../../../../domain/session';
+import { SessionEntity } from '../entities/session.entity';
+
+export class SessionMapper {
+ static toDomain(raw: SessionEntity): Session {
+ const session = new Session();
+ session.id = raw.id;
+ if (raw.user) {
+ session.user = UserMapper.toDomain(raw.user);
+ }
+ session.hash = raw.hash;
+ session.createdAt = raw.createdAt;
+ session.updatedAt = raw.updatedAt;
+ session.deletedAt = raw.deletedAt;
+ return session;
+ }
+
+ static toPersistence(session: Session): SessionEntity {
+ const user = new UserEntity();
+ user.id = Number(session.user.id);
+
+ const sessionEntity = new SessionEntity();
+ if (session.id && typeof session.id === 'number') {
+ sessionEntity.id = session.id;
+ }
+ sessionEntity.hash = session.hash;
+ sessionEntity.user = user;
+ sessionEntity.createdAt = session.createdAt;
+ sessionEntity.updatedAt = session.updatedAt;
+ sessionEntity.deletedAt = session.deletedAt;
+ return sessionEntity;
+ }
+}
diff --git a/src/session/infrastructure/persistence/relational/relational-persistence.module.ts b/src/session/infrastructure/persistence/relational/relational-persistence.module.ts
new file mode 100644
index 0000000..bdac604
--- /dev/null
+++ b/src/session/infrastructure/persistence/relational/relational-persistence.module.ts
@@ -0,0 +1,17 @@
+import { Module } from '@nestjs/common';
+import { SessionRepository } from '../session.repository';
+import { SessionRelationalRepository } from './repositories/session.repository';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { SessionEntity } from './entities/session.entity';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([SessionEntity])],
+ providers: [
+ {
+ provide: SessionRepository,
+ useClass: SessionRelationalRepository,
+ },
+ ],
+ exports: [SessionRepository],
+})
+export class RelationalSessionPersistenceModule {}
diff --git a/src/session/infrastructure/persistence/relational/repositories/session.repository.ts b/src/session/infrastructure/persistence/relational/repositories/session.repository.ts
new file mode 100644
index 0000000..84218dc
--- /dev/null
+++ b/src/session/infrastructure/persistence/relational/repositories/session.repository.ts
@@ -0,0 +1,85 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { FindOptionsWhere, Not, Repository } from 'typeorm';
+import { SessionEntity } from '../entities/session.entity';
+import { NullableType } from '../../../../../utils/types/nullable.type';
+
+import { SessionRepository } from '../../session.repository';
+import { Session } from '../../../../domain/session';
+
+import { SessionMapper } from '../mappers/session.mapper';
+import { User } from '../../../../../users/domain/user';
+import { UserEntity } from '../../../../../users/infrastructure/persistence/relational/entities/user.entity';
+import { EntityCondition } from '../../../../../utils/types/entity-condition.type';
+
+@Injectable()
+export class SessionRelationalRepository implements SessionRepository {
+ constructor(
+ @InjectRepository(SessionEntity)
+ private readonly sessionRepository: Repository,
+ ) {}
+
+ async findOne(
+ options: EntityCondition,
+ ): Promise> {
+ const entity = await this.sessionRepository.findOne({
+ where: options as FindOptionsWhere,
+ });
+
+ return entity ? SessionMapper.toDomain(entity) : null;
+ }
+
+ async create(data: Session): Promise {
+ const persistenceModel = SessionMapper.toPersistence(data);
+ return this.sessionRepository.save(
+ this.sessionRepository.create(persistenceModel),
+ );
+ }
+
+ async update(
+ id: Session['id'],
+ payload: Partial<
+ Omit
+ >,
+ ): Promise {
+ const entity = await this.sessionRepository.findOne({
+ where: { id: Number(id) },
+ });
+
+ if (!entity) {
+ throw new Error('Session not found');
+ }
+
+ const updatedEntity = await this.sessionRepository.save(
+ this.sessionRepository.create(
+ SessionMapper.toPersistence({
+ ...SessionMapper.toDomain(entity),
+ ...payload,
+ }),
+ ),
+ );
+
+ return SessionMapper.toDomain(updatedEntity);
+ }
+
+ async softDelete({
+ excludeId,
+ ...criteria
+ }: {
+ id?: Session['id'];
+ user?: Pick;
+ excludeId?: Session['id'];
+ }): Promise {
+ await this.sessionRepository.softDelete({
+ ...(criteria as {
+ id?: SessionEntity['id'];
+ user?: Pick;
+ }),
+ id: criteria.id
+ ? (criteria.id as SessionEntity['id'])
+ : excludeId
+ ? Not(excludeId as SessionEntity['id'])
+ : undefined,
+ });
+ }
+}
diff --git a/src/session/infrastructure/persistence/session.repository.ts b/src/session/infrastructure/persistence/session.repository.ts
new file mode 100644
index 0000000..3cd4e86
--- /dev/null
+++ b/src/session/infrastructure/persistence/session.repository.ts
@@ -0,0 +1,30 @@
+import { User } from '../../../users/domain/user';
+import { EntityCondition } from '../../../utils/types/entity-condition.type';
+import { NullableType } from '../../../utils/types/nullable.type';
+import { Session } from '../../domain/session';
+
+export abstract class SessionRepository {
+ abstract findOne(
+ options: EntityCondition,
+ ): Promise>;
+
+ abstract create(
+ data: Omit,
+ ): Promise;
+
+ abstract update(
+ id: Session['id'],
+ payload: Partial<
+ Omit
+ >,
+ ): Promise;
+
+ abstract softDelete({
+ excludeId,
+ ...criteria
+ }: {
+ id?: Session['id'];
+ user?: Pick;
+ excludeId?: Session['id'];
+ }): Promise;
+}
diff --git a/src/session/session.module.ts b/src/session/session.module.ts
new file mode 100644
index 0000000..e379fb3
--- /dev/null
+++ b/src/session/session.module.ts
@@ -0,0 +1,21 @@
+import { Module } from '@nestjs/common';
+
+import { DocumentSessionPersistenceModule } from './infrastructure/persistence/document/document-persistence.module';
+import { RelationalSessionPersistenceModule } from './infrastructure/persistence/relational/relational-persistence.module';
+import { SessionService } from './session.service';
+import { DatabaseConfig } from '../database/config/database-config.type';
+import databaseConfig from '../database/config/database.config';
+
+//
+const infrastructurePersistenceModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? DocumentSessionPersistenceModule
+ : RelationalSessionPersistenceModule;
+//
+
+@Module({
+ imports: [infrastructurePersistenceModule],
+ providers: [SessionService],
+ exports: [SessionService, infrastructurePersistenceModule],
+})
+export class SessionModule {}
diff --git a/src/session/session.service.ts b/src/session/session.service.ts
new file mode 100644
index 0000000..4ef1286
--- /dev/null
+++ b/src/session/session.service.ts
@@ -0,0 +1,39 @@
+import { Injectable } from '@nestjs/common';
+
+import { SessionRepository } from './infrastructure/persistence/session.repository';
+import { Session } from './domain/session';
+import { User } from '../users/domain/user';
+import { EntityCondition } from '../utils/types/entity-condition.type';
+import { NullableType } from '../utils/types/nullable.type';
+
+@Injectable()
+export class SessionService {
+ constructor(private readonly sessionRepository: SessionRepository) {}
+
+ findOne(options: EntityCondition): Promise> {
+ return this.sessionRepository.findOne(options);
+ }
+
+ create(
+ data: Omit,
+ ): Promise {
+ return this.sessionRepository.create(data);
+ }
+
+ update(
+ id: Session['id'],
+ payload: Partial<
+ Omit
+ >,
+ ): Promise {
+ return this.sessionRepository.update(id, payload);
+ }
+
+ async softDelete(criteria: {
+ id?: Session['id'];
+ user?: Pick;
+ excludeId?: Session['id'];
+ }): Promise {
+ await this.sessionRepository.softDelete(criteria);
+ }
+}
diff --git a/src/social/interfaces/social.interface.ts b/src/social/interfaces/social.interface.ts
new file mode 100644
index 0000000..90bb515
--- /dev/null
+++ b/src/social/interfaces/social.interface.ts
@@ -0,0 +1,6 @@
+export interface SocialInterface {
+ id: string;
+ firstName?: string;
+ lastName?: string;
+ email?: string;
+}
diff --git a/src/social/tokens.ts b/src/social/tokens.ts
new file mode 100644
index 0000000..4db5d01
--- /dev/null
+++ b/src/social/tokens.ts
@@ -0,0 +1,12 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Allow, IsNotEmpty } from 'class-validator';
+
+export class Tokens {
+ @ApiProperty()
+ @IsNotEmpty()
+ token1: string;
+
+ @Allow()
+ @ApiProperty()
+ token2?: string;
+}
diff --git a/src/statuses/domain/status.ts b/src/statuses/domain/status.ts
new file mode 100644
index 0000000..5625aba
--- /dev/null
+++ b/src/statuses/domain/status.ts
@@ -0,0 +1,25 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { Allow } from 'class-validator';
+import databaseConfig from '../../database/config/database.config';
+import { DatabaseConfig } from '../../database/config/database-config.type';
+
+//
+const idType = (databaseConfig() as DatabaseConfig).isDocumentDatabase
+ ? String
+ : Number;
+//
+
+export class Status {
+ @Allow()
+ @ApiResponseProperty({
+ type: idType,
+ })
+ id: number | string;
+
+ @Allow()
+ @ApiResponseProperty({
+ type: String,
+ example: 'active',
+ })
+ name?: string;
+}
diff --git a/src/statuses/dto/status.dto.ts b/src/statuses/dto/status.dto.ts
new file mode 100644
index 0000000..0fddd4e
--- /dev/null
+++ b/src/statuses/dto/status.dto.ts
@@ -0,0 +1,9 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Status } from '../domain/status';
+import { IsNumber } from 'class-validator';
+
+export class StatusDto implements Status {
+ @ApiProperty()
+ @IsNumber()
+ id: number;
+}
diff --git a/src/statuses/infrastructure/persistence/document/entities/status.schema.ts b/src/statuses/infrastructure/persistence/document/entities/status.schema.ts
new file mode 100644
index 0000000..34ffdc5
--- /dev/null
+++ b/src/statuses/infrastructure/persistence/document/entities/status.schema.ts
@@ -0,0 +1,14 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+export class StatusSchema {
+ @ApiResponseProperty({
+ type: String,
+ })
+ _id: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'active',
+ })
+ name?: string;
+}
diff --git a/src/statuses/infrastructure/persistence/relational/entities/status.entity.ts b/src/statuses/infrastructure/persistence/relational/entities/status.entity.ts
new file mode 100644
index 0000000..2c4d959
--- /dev/null
+++ b/src/statuses/infrastructure/persistence/relational/entities/status.entity.ts
@@ -0,0 +1,22 @@
+import { Column, Entity, PrimaryColumn } from 'typeorm';
+
+import { EntityRelationalHelper } from '../../../../../utils/relational-entity-helper';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+@Entity({
+ name: 'status',
+})
+export class StatusEntity extends EntityRelationalHelper {
+ @ApiResponseProperty({
+ type: Number,
+ })
+ @PrimaryColumn()
+ id: number;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'active',
+ })
+ @Column()
+ name?: string;
+}
diff --git a/src/statuses/statuses.enum.ts b/src/statuses/statuses.enum.ts
new file mode 100644
index 0000000..d80db88
--- /dev/null
+++ b/src/statuses/statuses.enum.ts
@@ -0,0 +1,4 @@
+export enum StatusEnum {
+ 'active' = 1,
+ 'inactive' = 2,
+}
diff --git a/src/users/domain/user.ts b/src/users/domain/user.ts
new file mode 100644
index 0000000..f5a92ad
--- /dev/null
+++ b/src/users/domain/user.ts
@@ -0,0 +1,83 @@
+import { Exclude, Expose } from 'class-transformer';
+import { FileType } from '../../files/domain/file';
+import { Role } from '../../roles/domain/role';
+import { Status } from '../../statuses/domain/status';
+import { ApiResponseProperty } from '@nestjs/swagger';
+import databaseConfig from '../../database/config/database.config';
+import { DatabaseConfig } from '../../database/config/database-config.type';
+
+//
+const idType = (databaseConfig() as DatabaseConfig).isDocumentDatabase
+ ? String
+ : Number;
+//
+
+export class User {
+ @ApiResponseProperty({
+ type: idType,
+ })
+ id: number | string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'john.doe@example.com',
+ })
+ @Expose({ groups: ['me', 'admin'] })
+ email: string | null;
+
+ @Exclude({ toPlainOnly: true })
+ password?: string;
+
+ @Exclude({ toPlainOnly: true })
+ previousPassword?: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'email',
+ })
+ @Expose({ groups: ['me', 'admin'] })
+ provider: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: '1234567890',
+ })
+ @Expose({ groups: ['me', 'admin'] })
+ socialId?: string | null;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'John',
+ })
+ firstName: string | null;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'Doe',
+ })
+ lastName: string | null;
+
+ @ApiResponseProperty({
+ type: () => FileType,
+ })
+ photo?: FileType | null;
+
+ @ApiResponseProperty({
+ type: () => Role,
+ })
+ role?: Role | null;
+
+ @ApiResponseProperty({
+ type: () => Status,
+ })
+ status?: Status;
+
+ @ApiResponseProperty()
+ createdAt: Date;
+
+ @ApiResponseProperty()
+ updatedAt: Date;
+
+ @ApiResponseProperty()
+ deletedAt: Date;
+}
diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts
new file mode 100644
index 0000000..736b93d
--- /dev/null
+++ b/src/users/dto/create-user.dto.ts
@@ -0,0 +1,47 @@
+import { Transform, Type } from 'class-transformer';
+import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
+import { IsEmail, IsNotEmpty, IsOptional, MinLength } from 'class-validator';
+import { FileDto } from '../../files/dto/file.dto';
+import { RoleDto } from '../../roles/dto/role.dto';
+import { StatusDto } from '../../statuses/dto/status.dto';
+import { lowerCaseTransformer } from '../../utils/transformers/lower-case.transformer';
+
+export class CreateUserDto {
+ @ApiProperty({ example: 'test1@example.com', type: String })
+ @Transform(lowerCaseTransformer)
+ @IsNotEmpty()
+ @IsEmail()
+ email: string | null;
+
+ @ApiProperty()
+ @MinLength(6)
+ password?: string;
+
+ provider?: string;
+
+ socialId?: string | null;
+
+ @ApiProperty({ example: 'John', type: String })
+ @IsNotEmpty()
+ firstName: string | null;
+
+ @ApiProperty({ example: 'Doe', type: String })
+ @IsNotEmpty()
+ lastName: string | null;
+
+ @ApiPropertyOptional({ type: () => FileDto })
+ @IsOptional()
+ photo?: FileDto | null;
+
+ @ApiPropertyOptional({ type: RoleDto })
+ @IsOptional()
+ @Type(() => RoleDto)
+ role?: RoleDto | null;
+
+ @ApiPropertyOptional({ type: StatusDto })
+ @IsOptional()
+ @Type(() => StatusDto)
+ status?: StatusDto;
+
+ hash?: string | null;
+}
diff --git a/src/users/dto/query-user.dto.ts b/src/users/dto/query-user.dto.ts
new file mode 100644
index 0000000..8c0d1ed
--- /dev/null
+++ b/src/users/dto/query-user.dto.ts
@@ -0,0 +1,61 @@
+import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
+import {
+ IsNumber,
+ IsOptional,
+ IsString,
+ ValidateNested,
+} from 'class-validator';
+import { Transform, Type, plainToInstance } from 'class-transformer';
+import { User } from '../domain/user';
+import { RoleDto } from '../../roles/dto/role.dto';
+
+export class FilterUserDto {
+ @ApiPropertyOptional({ type: RoleDto })
+ @IsOptional()
+ @ValidateNested({ each: true })
+ @Type(() => RoleDto)
+ roles?: RoleDto[] | null;
+}
+
+export class SortUserDto {
+ @ApiProperty()
+ @Type(() => String)
+ @IsString()
+ orderBy: keyof User;
+
+ @ApiProperty()
+ @IsString()
+ order: string;
+}
+
+export class QueryUserDto {
+ @ApiPropertyOptional()
+ @Transform(({ value }) => (value ? Number(value) : 1))
+ @IsNumber()
+ @IsOptional()
+ page?: number;
+
+ @ApiPropertyOptional()
+ @Transform(({ value }) => (value ? Number(value) : 10))
+ @IsNumber()
+ @IsOptional()
+ limit?: number;
+
+ @ApiPropertyOptional({ type: String })
+ @IsOptional()
+ @Transform(({ value }) =>
+ value ? plainToInstance(FilterUserDto, JSON.parse(value)) : undefined,
+ )
+ @ValidateNested()
+ @Type(() => FilterUserDto)
+ filters?: FilterUserDto | null;
+
+ @ApiPropertyOptional({ type: String })
+ @IsOptional()
+ @Transform(({ value }) => {
+ return value ? plainToInstance(SortUserDto, JSON.parse(value)) : undefined;
+ })
+ @ValidateNested({ each: true })
+ @Type(() => SortUserDto)
+ sort?: SortUserDto[] | null;
+}
diff --git a/src/users/dto/update-user.dto.ts b/src/users/dto/update-user.dto.ts
new file mode 100644
index 0000000..c16933c
--- /dev/null
+++ b/src/users/dto/update-user.dto.ts
@@ -0,0 +1,50 @@
+import { PartialType, ApiPropertyOptional } from '@nestjs/swagger';
+import { CreateUserDto } from './create-user.dto';
+
+import { Transform, Type } from 'class-transformer';
+import { IsEmail, IsOptional, MinLength } from 'class-validator';
+import { FileDto } from '../../files/dto/file.dto';
+import { RoleDto } from '../../roles/dto/role.dto';
+import { StatusDto } from '../../statuses/dto/status.dto';
+import { lowerCaseTransformer } from '../../utils/transformers/lower-case.transformer';
+
+export class UpdateUserDto extends PartialType(CreateUserDto) {
+ @ApiPropertyOptional({ example: 'test1@example.com', type: String })
+ @Transform(lowerCaseTransformer)
+ @IsOptional()
+ @IsEmail()
+ email?: string | null;
+
+ @ApiPropertyOptional()
+ @IsOptional()
+ @MinLength(6)
+ password?: string;
+
+ provider?: string;
+
+ socialId?: string | null;
+
+ @ApiPropertyOptional({ example: 'John', type: String })
+ @IsOptional()
+ firstName?: string | null;
+
+ @ApiPropertyOptional({ example: 'Doe', type: String })
+ @IsOptional()
+ lastName?: string | null;
+
+ @ApiPropertyOptional({ type: () => FileDto })
+ @IsOptional()
+ photo?: FileDto | null;
+
+ @ApiPropertyOptional({ type: () => RoleDto })
+ @IsOptional()
+ @Type(() => RoleDto)
+ role?: RoleDto | null;
+
+ @ApiPropertyOptional({ type: () => StatusDto })
+ @IsOptional()
+ @Type(() => StatusDto)
+ status?: StatusDto;
+
+ hash?: string | null;
+}
diff --git a/src/users/infrastructure/persistence/document/document-persistence.module.ts b/src/users/infrastructure/persistence/document/document-persistence.module.ts
new file mode 100644
index 0000000..d26ea4a
--- /dev/null
+++ b/src/users/infrastructure/persistence/document/document-persistence.module.ts
@@ -0,0 +1,21 @@
+import { Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { UserSchema, UserSchemaClass } from './entities/user.schema';
+import { UserRepository } from '../user.repository';
+import { UsersDocumentRepository } from './repositories/user.repository';
+
+@Module({
+ imports: [
+ MongooseModule.forFeature([
+ { name: UserSchemaClass.name, schema: UserSchema },
+ ]),
+ ],
+ providers: [
+ {
+ provide: UserRepository,
+ useClass: UsersDocumentRepository,
+ },
+ ],
+ exports: [UserRepository],
+})
+export class DocumentUserPersistenceModule {}
diff --git a/src/users/infrastructure/persistence/document/entities/user.schema.ts b/src/users/infrastructure/persistence/document/entities/user.schema.ts
new file mode 100644
index 0000000..dae8473
--- /dev/null
+++ b/src/users/infrastructure/persistence/document/entities/user.schema.ts
@@ -0,0 +1,126 @@
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+import { now, HydratedDocument } from 'mongoose';
+
+// We use class-transformer in schema and domain entity.
+// We duplicate these rules because you can choose not to use adapters
+// in your project and return an schema entity directly in response.
+import { Exclude, Expose, Type } from 'class-transformer';
+import { AuthProvidersEnum } from '../../../../../auth/auth-providers.enum';
+import { FileSchemaClass } from '../../../../../files/infrastructure/persistence/document/entities/file.schema';
+import { EntityDocumentHelper } from '../../../../../utils/document-entity-helper';
+import { StatusSchema } from '../../../../../statuses/infrastructure/persistence/document/entities/status.schema';
+import { RoleSchema } from '../../../../../roles/infrastructure/persistence/document/entities/role.schema';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+export type UserSchemaDocument = HydratedDocument;
+
+@Schema({
+ timestamps: true,
+ toJSON: {
+ virtuals: true,
+ getters: true,
+ },
+})
+export class UserSchemaClass extends EntityDocumentHelper {
+ @ApiResponseProperty({
+ type: String,
+ example: 'john.doe@example.com',
+ })
+ @Prop({
+ type: String,
+ unique: true,
+ })
+ @Expose({ groups: ['me', 'admin'], toPlainOnly: true })
+ email: string | null;
+
+ @Exclude({ toPlainOnly: true })
+ @Prop()
+ password?: string;
+
+ @Exclude({ toPlainOnly: true })
+ previousPassword?: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'email',
+ })
+ @Expose({ groups: ['me', 'admin'], toPlainOnly: true })
+ @Prop({
+ default: AuthProvidersEnum.email,
+ })
+ provider: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: '1234567890',
+ })
+ @Expose({ groups: ['me', 'admin'], toPlainOnly: true })
+ @Prop({
+ type: String,
+ default: null,
+ })
+ socialId?: string | null;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'John',
+ })
+ @Prop({
+ type: String,
+ })
+ firstName: string | null;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'Doe',
+ })
+ @Prop({
+ type: String,
+ })
+ lastName: string | null;
+
+ @ApiResponseProperty({
+ type: () => FileSchemaClass,
+ })
+ @Prop({
+ type: FileSchemaClass,
+ })
+ @Type(() => FileSchemaClass)
+ photo?: FileSchemaClass | null;
+
+ @ApiResponseProperty({
+ type: () => RoleSchema,
+ })
+ @Prop({
+ type: RoleSchema,
+ })
+ role?: RoleSchema | null;
+
+ @ApiResponseProperty({
+ type: () => StatusSchema,
+ })
+ @Prop({
+ type: StatusSchema,
+ })
+ status?: StatusSchema;
+
+ @ApiResponseProperty()
+ @Prop({ default: now })
+ createdAt: Date;
+
+ @ApiResponseProperty()
+ @Prop({ default: now })
+ updatedAt: Date;
+
+ @ApiResponseProperty()
+ @Prop()
+ deletedAt: Date;
+}
+
+export const UserSchema = SchemaFactory.createForClass(UserSchemaClass);
+
+UserSchema.virtual('previousPassword').get(function () {
+ return this.password;
+});
+
+UserSchema.index({ 'role._id': 1 });
diff --git a/src/users/infrastructure/persistence/document/mappers/user.mapper.ts b/src/users/infrastructure/persistence/document/mappers/user.mapper.ts
new file mode 100644
index 0000000..d3613aa
--- /dev/null
+++ b/src/users/infrastructure/persistence/document/mappers/user.mapper.ts
@@ -0,0 +1,85 @@
+import { User } from '../../../../domain/user';
+import { UserSchemaClass } from '../entities/user.schema';
+import { FileSchemaClass } from '../../../../../files/infrastructure/persistence/document/entities/file.schema';
+import { FileMapper } from '../../../../../files/infrastructure/persistence/document/mappers/file.mapper';
+import { Role } from '../../../../../roles/domain/role';
+import { Status } from '../../../../../statuses/domain/status';
+import { RoleSchema } from '../../../../../roles/infrastructure/persistence/document/entities/role.schema';
+import { StatusSchema } from '../../../../../statuses/infrastructure/persistence/document/entities/status.schema';
+
+export class UserMapper {
+ static toDomain(raw: UserSchemaClass): User {
+ const user = new User();
+ user.id = raw._id.toString();
+ user.email = raw.email;
+ user.password = raw.password;
+ user.previousPassword = raw.previousPassword;
+ user.provider = raw.provider;
+ user.socialId = raw.socialId;
+ user.firstName = raw.firstName;
+ user.lastName = raw.lastName;
+ if (raw.photo) {
+ user.photo = FileMapper.toDomain(raw.photo);
+ } else if (raw.photo === null) {
+ user.photo = null;
+ }
+
+ if (raw.role) {
+ user.role = new Role();
+ user.role.id = raw.role._id;
+ }
+
+ if (raw.status) {
+ user.status = new Status();
+ user.status.id = raw.status._id;
+ }
+
+ user.createdAt = raw.createdAt;
+ user.updatedAt = raw.updatedAt;
+ user.deletedAt = raw.deletedAt;
+ return user;
+ }
+
+ static toPersistence(user: User): UserSchemaClass {
+ let role: RoleSchema | undefined = undefined;
+
+ if (user.role) {
+ role = new RoleSchema();
+ role._id = user.role.id.toString();
+ }
+
+ let photo: FileSchemaClass | undefined = undefined;
+
+ if (user.photo) {
+ photo = new FileSchemaClass();
+ photo._id = user.photo.id;
+ photo.path = user.photo.path;
+ }
+
+ let status: StatusSchema | undefined = undefined;
+
+ if (user.status) {
+ status = new StatusSchema();
+ status._id = user.status.id.toString();
+ }
+
+ const userEntity = new UserSchemaClass();
+ if (user.id && typeof user.id === 'string') {
+ userEntity._id = user.id;
+ }
+ userEntity.email = user.email;
+ userEntity.password = user.password;
+ userEntity.previousPassword = user.previousPassword;
+ userEntity.provider = user.provider;
+ userEntity.socialId = user.socialId;
+ userEntity.firstName = user.firstName;
+ userEntity.lastName = user.lastName;
+ userEntity.photo = photo;
+ userEntity.role = role;
+ userEntity.status = status;
+ userEntity.createdAt = user.createdAt;
+ userEntity.updatedAt = user.updatedAt;
+ userEntity.deletedAt = user.deletedAt;
+ return userEntity;
+ }
+}
diff --git a/src/users/infrastructure/persistence/document/repositories/user.repository.ts b/src/users/infrastructure/persistence/document/repositories/user.repository.ts
new file mode 100644
index 0000000..696805f
--- /dev/null
+++ b/src/users/infrastructure/persistence/document/repositories/user.repository.ts
@@ -0,0 +1,97 @@
+import { Injectable } from '@nestjs/common';
+
+import { NullableType } from '../../../../../utils/types/nullable.type';
+import { FilterUserDto, SortUserDto } from '../../../../dto/query-user.dto';
+import { User } from '../../../../domain/user';
+import { UserRepository } from '../../user.repository';
+import { UserSchemaClass } from '../entities/user.schema';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model } from 'mongoose';
+import { UserMapper } from '../mappers/user.mapper';
+import { EntityCondition } from '../../../../../utils/types/entity-condition.type';
+import { IPaginationOptions } from '../../../../../utils/types/pagination-options';
+import domainToDocumentCondition from '../../../../../utils/domain-to-document-condition';
+
+@Injectable()
+export class UsersDocumentRepository implements UserRepository {
+ constructor(
+ @InjectModel(UserSchemaClass.name)
+ private readonly usersModel: Model,
+ ) {}
+
+ async create(data: User): Promise {
+ const persistenceModel = UserMapper.toPersistence(data);
+ const createdUser = new this.usersModel(persistenceModel);
+ const userObject = await createdUser.save();
+ return UserMapper.toDomain(userObject);
+ }
+
+ async findManyWithPagination({
+ filterOptions,
+ sortOptions,
+ paginationOptions,
+ }: {
+ filterOptions?: FilterUserDto | null;
+ sortOptions?: SortUserDto[] | null;
+ paginationOptions: IPaginationOptions;
+ }): Promise {
+ const where: EntityCondition = {};
+ if (filterOptions?.roles?.length) {
+ where.role = filterOptions.roles.map((role) => ({
+ id: role.id.toString(),
+ }));
+ }
+
+ const userObjects = await this.usersModel
+ .find(domainToDocumentCondition(where))
+ .sort(
+ sortOptions?.reduce(
+ (accumulator, sort) => ({
+ ...accumulator,
+ [sort.orderBy === 'id' ? '_id' : sort.orderBy]:
+ sort.order.toUpperCase() === 'ASC' ? 1 : -1,
+ }),
+ {},
+ ),
+ )
+ .skip((paginationOptions.page - 1) * paginationOptions.limit)
+ .limit(paginationOptions.limit);
+
+ return userObjects.map((userObject) => UserMapper.toDomain(userObject));
+ }
+
+ async findOne(fields: EntityCondition): Promise> {
+ const userObject = await this.usersModel.findOne(
+ domainToDocumentCondition(fields),
+ );
+ return userObject ? UserMapper.toDomain(userObject) : null;
+ }
+
+ async update(id: User['id'], payload: Partial): Promise {
+ const clonedPayload = { ...payload };
+ delete clonedPayload.id;
+
+ const filter = { _id: id.toString() };
+ const user = await this.usersModel.findOne(filter);
+
+ if (!user) {
+ return null;
+ }
+
+ const userObject = await this.usersModel.findOneAndUpdate(
+ filter,
+ UserMapper.toPersistence({
+ ...UserMapper.toDomain(user),
+ ...clonedPayload,
+ }),
+ );
+
+ return userObject ? UserMapper.toDomain(userObject) : null;
+ }
+
+ async softDelete(id: User['id']): Promise {
+ await this.usersModel.deleteOne({
+ _id: id.toString(),
+ });
+ }
+}
diff --git a/src/users/infrastructure/persistence/relational/entities/user.entity.ts b/src/users/infrastructure/persistence/relational/entities/user.entity.ts
new file mode 100644
index 0000000..f20c47e
--- /dev/null
+++ b/src/users/infrastructure/persistence/relational/entities/user.entity.ts
@@ -0,0 +1,128 @@
+import {
+ Column,
+ AfterLoad,
+ CreateDateColumn,
+ DeleteDateColumn,
+ Entity,
+ Index,
+ ManyToOne,
+ PrimaryGeneratedColumn,
+ UpdateDateColumn,
+ JoinColumn,
+ OneToOne,
+} from 'typeorm';
+import { RoleEntity } from '../../../../../roles/infrastructure/persistence/relational/entities/role.entity';
+import { StatusEntity } from '../../../../../statuses/infrastructure/persistence/relational/entities/status.entity';
+import { FileEntity } from '../../../../../files/infrastructure/persistence/relational/entities/file.entity';
+
+import { AuthProvidersEnum } from '../../../../../auth/auth-providers.enum';
+import { EntityRelationalHelper } from '../../../../../utils/relational-entity-helper';
+
+// We use class-transformer in ORM entity and domain entity.
+// We duplicate these rules because you can choose not to use adapters
+// in your project and return an ORM entity directly in response.
+import { Exclude, Expose } from 'class-transformer';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+@Entity({
+ name: 'user',
+})
+export class UserEntity extends EntityRelationalHelper {
+ @ApiResponseProperty({
+ type: Number,
+ })
+ @PrimaryGeneratedColumn()
+ id: number;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'john.doe@example.com',
+ })
+ // For "string | null" we need to use String type.
+ // More info: https://github.com/typeorm/typeorm/issues/2567
+ @Column({ type: String, unique: true, nullable: true })
+ @Expose({ groups: ['me', 'admin'] })
+ email: string | null;
+
+ @Column({ nullable: true })
+ @Exclude({ toPlainOnly: true })
+ password?: string;
+
+ @Exclude({ toPlainOnly: true })
+ public previousPassword?: string;
+
+ @AfterLoad()
+ public loadPreviousPassword(): void {
+ this.previousPassword = this.password;
+ }
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'email',
+ })
+ @Column({ default: AuthProvidersEnum.email })
+ @Expose({ groups: ['me', 'admin'] })
+ provider: string;
+
+ @ApiResponseProperty({
+ type: String,
+ example: '1234567890',
+ })
+ @Index()
+ @Column({ type: String, nullable: true })
+ @Expose({ groups: ['me', 'admin'] })
+ socialId?: string | null;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'John',
+ })
+ @Index()
+ @Column({ type: String, nullable: true })
+ firstName: string | null;
+
+ @ApiResponseProperty({
+ type: String,
+ example: 'Doe',
+ })
+ @Index()
+ @Column({ type: String, nullable: true })
+ lastName: string | null;
+
+ @ApiResponseProperty({
+ type: () => FileEntity,
+ })
+ @OneToOne(() => FileEntity, {
+ eager: true,
+ })
+ @JoinColumn()
+ photo?: FileEntity | null;
+
+ @ApiResponseProperty({
+ type: () => RoleEntity,
+ })
+ @ManyToOne(() => RoleEntity, {
+ eager: true,
+ })
+ role?: RoleEntity | null;
+
+ @ApiResponseProperty({
+ type: () => StatusEntity,
+ })
+ @ManyToOne(() => StatusEntity, {
+ eager: true,
+ })
+ status?: StatusEntity;
+
+ @ApiResponseProperty()
+ @CreateDateColumn()
+ createdAt: Date;
+
+ @ApiResponseProperty()
+ @UpdateDateColumn()
+ updatedAt: Date;
+
+ @ApiResponseProperty()
+ @DeleteDateColumn()
+ deletedAt: Date;
+}
diff --git a/src/users/infrastructure/persistence/relational/mappers/user.mapper.ts b/src/users/infrastructure/persistence/relational/mappers/user.mapper.ts
new file mode 100644
index 0000000..c459583
--- /dev/null
+++ b/src/users/infrastructure/persistence/relational/mappers/user.mapper.ts
@@ -0,0 +1,74 @@
+import { FileEntity } from '../../../../../files/infrastructure/persistence/relational/entities/file.entity';
+import { FileMapper } from '../../../../../files/infrastructure/persistence/relational/mappers/file.mapper';
+import { RoleEntity } from '../../../../../roles/infrastructure/persistence/relational/entities/role.entity';
+import { StatusEntity } from '../../../../../statuses/infrastructure/persistence/relational/entities/status.entity';
+import { User } from '../../../../domain/user';
+import { UserEntity } from '../entities/user.entity';
+
+export class UserMapper {
+ static toDomain(raw: UserEntity): User {
+ const user = new User();
+ user.id = raw.id;
+ user.email = raw.email;
+ user.password = raw.password;
+ user.previousPassword = raw.previousPassword;
+ user.provider = raw.provider;
+ user.socialId = raw.socialId;
+ user.firstName = raw.firstName;
+ user.lastName = raw.lastName;
+ if (raw.photo) {
+ user.photo = FileMapper.toDomain(raw.photo);
+ }
+ user.role = raw.role;
+ user.status = raw.status;
+ user.createdAt = raw.createdAt;
+ user.updatedAt = raw.updatedAt;
+ user.deletedAt = raw.deletedAt;
+ return user;
+ }
+
+ static toPersistence(user: User): UserEntity {
+ let role: RoleEntity | undefined = undefined;
+
+ if (user.role) {
+ role = new RoleEntity();
+ role.id = Number(user.role.id);
+ }
+
+ let photo: FileEntity | undefined | null = undefined;
+
+ if (user.photo) {
+ photo = new FileEntity();
+ photo.id = user.photo.id;
+ photo.path = user.photo.path;
+ } else if (user.photo === null) {
+ photo = null;
+ }
+
+ let status: StatusEntity | undefined = undefined;
+
+ if (user.status) {
+ status = new StatusEntity();
+ status.id = Number(user.status.id);
+ }
+
+ const userEntity = new UserEntity();
+ if (user.id && typeof user.id === 'number') {
+ userEntity.id = user.id;
+ }
+ userEntity.email = user.email;
+ userEntity.password = user.password;
+ userEntity.previousPassword = user.previousPassword;
+ userEntity.provider = user.provider;
+ userEntity.socialId = user.socialId;
+ userEntity.firstName = user.firstName;
+ userEntity.lastName = user.lastName;
+ userEntity.photo = photo;
+ userEntity.role = role;
+ userEntity.status = status;
+ userEntity.createdAt = user.createdAt;
+ userEntity.updatedAt = user.updatedAt;
+ userEntity.deletedAt = user.deletedAt;
+ return userEntity;
+ }
+}
diff --git a/src/users/infrastructure/persistence/relational/relational-persistence.module.ts b/src/users/infrastructure/persistence/relational/relational-persistence.module.ts
new file mode 100644
index 0000000..d1a76cd
--- /dev/null
+++ b/src/users/infrastructure/persistence/relational/relational-persistence.module.ts
@@ -0,0 +1,17 @@
+import { Module } from '@nestjs/common';
+import { UserRepository } from '../user.repository';
+import { UsersRelationalRepository } from './repositories/user.repository';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { UserEntity } from './entities/user.entity';
+
+@Module({
+ imports: [TypeOrmModule.forFeature([UserEntity])],
+ providers: [
+ {
+ provide: UserRepository,
+ useClass: UsersRelationalRepository,
+ },
+ ],
+ exports: [UserRepository],
+})
+export class RelationalUserPersistenceModule {}
diff --git a/src/users/infrastructure/persistence/relational/repositories/user.repository.ts b/src/users/infrastructure/persistence/relational/repositories/user.repository.ts
new file mode 100644
index 0000000..3b71771
--- /dev/null
+++ b/src/users/infrastructure/persistence/relational/repositories/user.repository.ts
@@ -0,0 +1,93 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+
+import { FindOptionsWhere, Repository } from 'typeorm';
+import { UserEntity } from '../entities/user.entity';
+import { NullableType } from '../../../../../utils/types/nullable.type';
+import { FilterUserDto, SortUserDto } from '../../../../dto/query-user.dto';
+import { User } from '../../../../domain/user';
+import { UserRepository } from '../../user.repository';
+import { UserMapper } from '../mappers/user.mapper';
+import { EntityCondition } from '../../../../../utils/types/entity-condition.type';
+import { IPaginationOptions } from '../../../../../utils/types/pagination-options';
+
+@Injectable()
+export class UsersRelationalRepository implements UserRepository {
+ constructor(
+ @InjectRepository(UserEntity)
+ private readonly usersRepository: Repository,
+ ) {}
+
+ async create(data: User): Promise {
+ const persistenceModel = UserMapper.toPersistence(data);
+ const newEntity = await this.usersRepository.save(
+ this.usersRepository.create(persistenceModel),
+ );
+ return UserMapper.toDomain(newEntity);
+ }
+
+ async findManyWithPagination({
+ filterOptions,
+ sortOptions,
+ paginationOptions,
+ }: {
+ filterOptions?: FilterUserDto | null;
+ sortOptions?: SortUserDto[] | null;
+ paginationOptions: IPaginationOptions;
+ }): Promise {
+ const where: FindOptionsWhere = {};
+ if (filterOptions?.roles?.length) {
+ where.role = filterOptions.roles.map((role) => ({
+ id: role.id,
+ }));
+ }
+
+ const entities = await this.usersRepository.find({
+ skip: (paginationOptions.page - 1) * paginationOptions.limit,
+ take: paginationOptions.limit,
+ where: where,
+ order: sortOptions?.reduce(
+ (accumulator, sort) => ({
+ ...accumulator,
+ [sort.orderBy]: sort.order,
+ }),
+ {},
+ ),
+ });
+
+ return entities.map((user) => UserMapper.toDomain(user));
+ }
+
+ async findOne(fields: EntityCondition): Promise> {
+ const entity = await this.usersRepository.findOne({
+ where: fields as FindOptionsWhere,
+ });
+
+ return entity ? UserMapper.toDomain(entity) : null;
+ }
+
+ async update(id: User['id'], payload: Partial): Promise {
+ const entity = await this.usersRepository.findOne({
+ where: { id: Number(id) },
+ });
+
+ if (!entity) {
+ throw new Error('User not found');
+ }
+
+ const updatedEntity = await this.usersRepository.save(
+ this.usersRepository.create(
+ UserMapper.toPersistence({
+ ...UserMapper.toDomain(entity),
+ ...payload,
+ }),
+ ),
+ );
+
+ return UserMapper.toDomain(updatedEntity);
+ }
+
+ async softDelete(id: User['id']): Promise {
+ await this.usersRepository.softDelete(id);
+ }
+}
diff --git a/src/users/infrastructure/persistence/user.repository.ts b/src/users/infrastructure/persistence/user.repository.ts
new file mode 100644
index 0000000..641eba4
--- /dev/null
+++ b/src/users/infrastructure/persistence/user.repository.ts
@@ -0,0 +1,32 @@
+import { DeepPartial } from '../../../utils/types/deep-partial.type';
+import { EntityCondition } from '../../../utils/types/entity-condition.type';
+import { NullableType } from '../../../utils/types/nullable.type';
+import { IPaginationOptions } from '../../../utils/types/pagination-options';
+import { User } from '../../domain/user';
+
+import { FilterUserDto, SortUserDto } from '../../dto/query-user.dto';
+
+export abstract class UserRepository {
+ abstract create(
+ data: Omit,
+ ): Promise;
+
+ abstract findManyWithPagination({
+ filterOptions,
+ sortOptions,
+ paginationOptions,
+ }: {
+ filterOptions?: FilterUserDto | null;
+ sortOptions?: SortUserDto[] | null;
+ paginationOptions: IPaginationOptions;
+ }): Promise;
+
+ abstract findOne(fields: EntityCondition): Promise>;
+
+ abstract update(
+ id: User['id'],
+ payload: DeepPartial,
+ ): Promise;
+
+ abstract softDelete(id: User['id']): Promise;
+}
diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts
new file mode 100644
index 0000000..90b32ee
--- /dev/null
+++ b/src/users/users.controller.ts
@@ -0,0 +1,139 @@
+import {
+ Controller,
+ Get,
+ Post,
+ Body,
+ Patch,
+ Param,
+ Delete,
+ UseGuards,
+ Query,
+ HttpStatus,
+ HttpCode,
+ SerializeOptions,
+} from '@nestjs/common';
+import { CreateUserDto } from './dto/create-user.dto';
+import { UpdateUserDto } from './dto/update-user.dto';
+import {
+ ApiBearerAuth,
+ ApiCreatedResponse,
+ ApiOkResponse,
+ ApiParam,
+ ApiTags,
+} from '@nestjs/swagger';
+import { Roles } from '../roles/roles.decorator';
+import { RoleEnum } from '../roles/roles.enum';
+import { AuthGuard } from '@nestjs/passport';
+
+import {
+ InfinityPaginationResponse,
+ InfinityPaginationResponseDto,
+} from '../utils/dto/infinity-pagination-response.dto';
+import { NullableType } from '../utils/types/nullable.type';
+import { QueryUserDto } from './dto/query-user.dto';
+import { User } from './domain/user';
+import { UsersService } from './users.service';
+import { RolesGuard } from '../roles/roles.guard';
+import { infinityPagination } from '../utils/infinity-pagination';
+
+@ApiBearerAuth()
+@Roles(RoleEnum.admin)
+@UseGuards(AuthGuard('jwt'), RolesGuard)
+@ApiTags('Users')
+@Controller({
+ path: 'users',
+ version: '1',
+})
+export class UsersController {
+ constructor(private readonly usersService: UsersService) {}
+
+ @ApiCreatedResponse({
+ type: User,
+ })
+ @SerializeOptions({
+ groups: ['admin'],
+ })
+ @Post()
+ @HttpCode(HttpStatus.CREATED)
+ create(@Body() createProfileDto: CreateUserDto): Promise {
+ return this.usersService.create(createProfileDto);
+ }
+
+ @ApiOkResponse({
+ type: InfinityPaginationResponse(User),
+ })
+ @SerializeOptions({
+ groups: ['admin'],
+ })
+ @Get()
+ @HttpCode(HttpStatus.OK)
+ async findAll(
+ @Query() query: QueryUserDto,
+ ): Promise> {
+ const page = query?.page ?? 1;
+ let limit = query?.limit ?? 10;
+ if (limit > 50) {
+ limit = 50;
+ }
+
+ return infinityPagination(
+ await this.usersService.findManyWithPagination({
+ filterOptions: query?.filters,
+ sortOptions: query?.sort,
+ paginationOptions: {
+ page,
+ limit,
+ },
+ }),
+ { page, limit },
+ );
+ }
+
+ @ApiOkResponse({
+ type: User,
+ })
+ @SerializeOptions({
+ groups: ['admin'],
+ })
+ @Get(':id')
+ @HttpCode(HttpStatus.OK)
+ @ApiParam({
+ name: 'id',
+ type: String,
+ required: true,
+ })
+ findOne(@Param('id') id: User['id']): Promise> {
+ return this.usersService.findOne({ id });
+ }
+
+ @ApiOkResponse({
+ type: User,
+ })
+ @SerializeOptions({
+ groups: ['admin'],
+ })
+ @Patch(':id')
+ @HttpCode(HttpStatus.OK)
+ @ApiParam({
+ name: 'id',
+ type: String,
+ required: true,
+ })
+ update(
+ @Param('id') id: User['id'],
+ @Body() updateProfileDto: UpdateUserDto,
+ ): Promise {
+ return this.usersService.update(id, updateProfileDto);
+ }
+
+ @Delete(':id')
+ @ApiParam({
+ name: 'id',
+ type: String,
+ required: true,
+ })
+ @HttpCode(HttpStatus.NO_CONTENT)
+ remove(@Param('id') id: User['id']): Promise {
+ return this.usersService.softDelete(id);
+ }
+}
diff --git a/src/users/users.module.ts b/src/users/users.module.ts
new file mode 100644
index 0000000..5fac184
--- /dev/null
+++ b/src/users/users.module.ts
@@ -0,0 +1,25 @@
+import { Module } from '@nestjs/common';
+
+import { UsersController } from './users.controller';
+
+import { UsersService } from './users.service';
+import { DocumentUserPersistenceModule } from './infrastructure/persistence/document/document-persistence.module';
+import { RelationalUserPersistenceModule } from './infrastructure/persistence/relational/relational-persistence.module';
+import { DatabaseConfig } from '../database/config/database-config.type';
+import databaseConfig from '../database/config/database.config';
+import { FilesModule } from '../files/files.module';
+
+//
+const infrastructurePersistenceModule = (databaseConfig() as DatabaseConfig)
+ .isDocumentDatabase
+ ? DocumentUserPersistenceModule
+ : RelationalUserPersistenceModule;
+//
+
+@Module({
+ imports: [infrastructurePersistenceModule, FilesModule],
+ controllers: [UsersController],
+ providers: [UsersService],
+ exports: [UsersService, infrastructurePersistenceModule],
+})
+export class UsersModule {}
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
new file mode 100644
index 0000000..2fabc95
--- /dev/null
+++ b/src/users/users.service.ts
@@ -0,0 +1,196 @@
+import {
+ HttpStatus,
+ Injectable,
+ UnprocessableEntityException,
+} from '@nestjs/common';
+import { CreateUserDto } from './dto/create-user.dto';
+import { NullableType } from '../utils/types/nullable.type';
+import { FilterUserDto, SortUserDto } from './dto/query-user.dto';
+import { UserRepository } from './infrastructure/persistence/user.repository';
+import { User } from './domain/user';
+import bcrypt from 'bcryptjs';
+import { AuthProvidersEnum } from '../auth/auth-providers.enum';
+import { FilesService } from '../files/files.service';
+import { RoleEnum } from '../roles/roles.enum';
+import { StatusEnum } from '../statuses/statuses.enum';
+import { EntityCondition } from '../utils/types/entity-condition.type';
+import { IPaginationOptions } from '../utils/types/pagination-options';
+import { DeepPartial } from '../utils/types/deep-partial.type';
+
+@Injectable()
+export class UsersService {
+ constructor(
+ private readonly usersRepository: UserRepository,
+ private readonly filesService: FilesService,
+ ) {}
+
+ async create(createProfileDto: CreateUserDto): Promise {
+ const clonedPayload = {
+ provider: AuthProvidersEnum.email,
+ ...createProfileDto,
+ };
+
+ if (clonedPayload.password) {
+ const salt = await bcrypt.genSalt();
+ clonedPayload.password = await bcrypt.hash(clonedPayload.password, salt);
+ }
+
+ if (clonedPayload.email) {
+ const userObject = await this.usersRepository.findOne({
+ email: clonedPayload.email,
+ });
+ if (userObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ email: 'emailAlreadyExists',
+ },
+ });
+ }
+ }
+
+ if (clonedPayload.photo?.id) {
+ const fileObject = await this.filesService.findOne({
+ id: clonedPayload.photo.id,
+ });
+ if (!fileObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ photo: 'imageNotExists',
+ },
+ });
+ }
+ clonedPayload.photo = fileObject;
+ }
+
+ if (clonedPayload.role?.id) {
+ const roleObject = Object.values(RoleEnum)
+ .map(String)
+ .includes(String(clonedPayload.role.id));
+ if (!roleObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ role: 'roleNotExists',
+ },
+ });
+ }
+ }
+
+ if (clonedPayload.status?.id) {
+ const statusObject = Object.values(StatusEnum)
+ .map(String)
+ .includes(String(clonedPayload.status.id));
+ if (!statusObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ status: 'statusNotExists',
+ },
+ });
+ }
+ }
+
+ return this.usersRepository.create(clonedPayload);
+ }
+
+ findManyWithPagination({
+ filterOptions,
+ sortOptions,
+ paginationOptions,
+ }: {
+ filterOptions?: FilterUserDto | null;
+ sortOptions?: SortUserDto[] | null;
+ paginationOptions: IPaginationOptions;
+ }): Promise {
+ return this.usersRepository.findManyWithPagination({
+ filterOptions,
+ sortOptions,
+ paginationOptions,
+ });
+ }
+
+ findOne(fields: EntityCondition): Promise> {
+ return this.usersRepository.findOne(fields);
+ }
+
+ async update(
+ id: User['id'],
+ payload: DeepPartial,
+ ): Promise {
+ const clonedPayload = { ...payload };
+
+ if (
+ clonedPayload.password &&
+ clonedPayload.previousPassword !== clonedPayload.password
+ ) {
+ const salt = await bcrypt.genSalt();
+ clonedPayload.password = await bcrypt.hash(clonedPayload.password, salt);
+ }
+
+ if (clonedPayload.email) {
+ const userObject = await this.usersRepository.findOne({
+ email: clonedPayload.email,
+ });
+
+ if (userObject && userObject.id !== id) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ email: 'emailAlreadyExists',
+ },
+ });
+ }
+ }
+
+ if (clonedPayload.photo?.id) {
+ const fileObject = await this.filesService.findOne({
+ id: clonedPayload.photo.id,
+ });
+ if (!fileObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ photo: 'imageNotExists',
+ },
+ });
+ }
+ clonedPayload.photo = fileObject;
+ }
+
+ if (clonedPayload.role?.id) {
+ const roleObject = Object.values(RoleEnum)
+ .map(String)
+ .includes(String(clonedPayload.role.id));
+ if (!roleObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ role: 'roleNotExists',
+ },
+ });
+ }
+ }
+
+ if (clonedPayload.status?.id) {
+ const statusObject = Object.values(StatusEnum)
+ .map(String)
+ .includes(String(clonedPayload.status.id));
+ if (!statusObject) {
+ throw new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: {
+ status: 'statusNotExists',
+ },
+ });
+ }
+ }
+
+ return this.usersRepository.update(id, clonedPayload);
+ }
+
+ async softDelete(id: User['id']): Promise {
+ await this.usersRepository.softDelete(id);
+ }
+}
diff --git a/src/utils/deep-resolver.ts b/src/utils/deep-resolver.ts
new file mode 100644
index 0000000..28fe339
--- /dev/null
+++ b/src/utils/deep-resolver.ts
@@ -0,0 +1,30 @@
+async function deepResolvePromises(input) {
+ if (input instanceof Promise) {
+ return await input;
+ }
+
+ if (Array.isArray(input)) {
+ const resolvedArray = await Promise.all(input.map(deepResolvePromises));
+ return resolvedArray;
+ }
+
+ if (input instanceof Date) {
+ return input;
+ }
+
+ if (typeof input === 'object' && input !== null) {
+ const keys = Object.keys(input);
+ const resolvedObject = {};
+
+ for (const key of keys) {
+ const resolvedValue = await deepResolvePromises(input[key]);
+ resolvedObject[key] = resolvedValue;
+ }
+
+ return resolvedObject;
+ }
+
+ return input;
+}
+
+export default deepResolvePromises;
diff --git a/src/utils/document-entity-helper.ts b/src/utils/document-entity-helper.ts
new file mode 100644
index 0000000..54a8c32
--- /dev/null
+++ b/src/utils/document-entity-helper.ts
@@ -0,0 +1,22 @@
+import { ApiResponseProperty } from '@nestjs/swagger';
+import { Transform } from 'class-transformer';
+
+export class EntityDocumentHelper {
+ @ApiResponseProperty({
+ type: String,
+ })
+ @Transform(
+ (value) => {
+ if ('value' in value) {
+ // https://github.com/typestack/class-transformer/issues/879
+ return value.obj[value.key].toString();
+ }
+
+ return 'unknown value';
+ },
+ {
+ toPlainOnly: true,
+ },
+ )
+ public _id: string;
+}
diff --git a/src/utils/domain-to-document-condition.spec.ts b/src/utils/domain-to-document-condition.spec.ts
new file mode 100644
index 0000000..aa1cfe8
--- /dev/null
+++ b/src/utils/domain-to-document-condition.spec.ts
@@ -0,0 +1,27 @@
+import domainToDocumentCondition from './domain-to-document-condition';
+
+describe('domainToDocumentCondition', () => {
+ it('should convert domain to document conditions', () => {
+ const conditions = {
+ id: 'abc',
+ email: 'test@example.com',
+ role: [{ id: '1' }],
+ status: {
+ id: '2',
+ },
+ keyLevel1: {
+ id: '3',
+ keyLevel2: [{ id: '4' }, { id: '5' }],
+ },
+ };
+
+ expect(domainToDocumentCondition(conditions)).toEqual({
+ _id: 'abc',
+ email: 'test@example.com',
+ 'role._id': { $in: ['1'] },
+ 'status._id': '2',
+ 'keyLevel1._id': '3',
+ 'keyLevel1.keyLevel2._id': { $in: ['4', '5'] },
+ });
+ });
+});
diff --git a/src/utils/domain-to-document-condition.ts b/src/utils/domain-to-document-condition.ts
new file mode 100644
index 0000000..d137437
--- /dev/null
+++ b/src/utils/domain-to-document-condition.ts
@@ -0,0 +1,73 @@
+import { EntityCondition } from './types/entity-condition.type';
+
+function toDocumentCondition(
+ conditionObject: T,
+ parentKey: string = '',
+ isArray = false,
+): Record }> {
+ if (isArray && Array.isArray(conditionObject)) {
+ const keys = [
+ ...new Set(conditionObject.map((value) => Object.keys(value)).flat()),
+ ];
+
+ return keys.reduce((acc, key) => {
+ const documentKey = key === 'id' ? '_id' : key;
+ const newKey = parentKey ? `${parentKey}.${documentKey}` : documentKey;
+ const values = conditionObject.map((value) => value[key]);
+
+ if (values.every((value) => value === null || value === undefined)) {
+ return acc;
+ }
+
+ if (values.every((value) => value instanceof Date)) {
+ return {
+ ...acc,
+ [newKey]: {
+ $in: values.map((value) => (value as Date).toISOString()),
+ },
+ };
+ }
+
+ if (values.every((value) => Array.isArray(value))) {
+ return {
+ ...acc,
+ [newKey]: {
+ $in: values.flat(),
+ },
+ };
+ }
+
+ return { ...acc, [newKey]: { $in: values } };
+ }, {});
+ }
+
+ return Object.keys(conditionObject).reduce((acc, key) => {
+ const documentKey = key === 'id' ? '_id' : key;
+ const value = conditionObject[key];
+ const newKey = parentKey ? `${parentKey}.${documentKey}` : documentKey;
+
+ if (value === null || value === undefined) {
+ return acc;
+ }
+
+ if (value instanceof Date) {
+ return { ...acc, [newKey]: value.toISOString() };
+ }
+
+ if (Array.isArray(value)) {
+ return { ...acc, ...toDocumentCondition(value, newKey, true) };
+ }
+
+ if (value instanceof Object) {
+ return { ...acc, ...toDocumentCondition(value, newKey) };
+ }
+
+ return { ...acc, [newKey]: value };
+ }, {});
+}
+
+function domainToDocumentCondition(conditions: EntityCondition) {
+ return toDocumentCondition(conditions);
+}
+
+export default domainToDocumentCondition;
diff --git a/src/utils/dto/infinity-pagination-response.dto.ts b/src/utils/dto/infinity-pagination-response.dto.ts
new file mode 100644
index 0000000..ada3f77
--- /dev/null
+++ b/src/utils/dto/infinity-pagination-response.dto.ts
@@ -0,0 +1,27 @@
+import { Type } from '@nestjs/common';
+import { ApiResponseProperty } from '@nestjs/swagger';
+
+export class InfinityPaginationResponseDto {
+ data: T[];
+ hasNextPage: boolean;
+}
+
+export function InfinityPaginationResponse(classReference: Type) {
+ abstract class Pagination {
+ @ApiResponseProperty({ type: [classReference] })
+ data!: T[];
+
+ @ApiResponseProperty({
+ type: Boolean,
+ example: true,
+ })
+ hasNextPage: boolean;
+ }
+
+ Object.defineProperty(Pagination, 'name', {
+ writable: false,
+ value: `InfinityPagination${classReference.name}ResponseDto`,
+ });
+
+ return Pagination;
+}
diff --git a/src/utils/infinity-pagination.ts b/src/utils/infinity-pagination.ts
new file mode 100644
index 0000000..f5d713c
--- /dev/null
+++ b/src/utils/infinity-pagination.ts
@@ -0,0 +1,12 @@
+import { IPaginationOptions } from './types/pagination-options';
+import { InfinityPaginationResponseDto } from './dto/infinity-pagination-response.dto';
+
+export const infinityPagination = (
+ data: T[],
+ options: IPaginationOptions,
+): InfinityPaginationResponseDto => {
+ return {
+ data,
+ hasNextPage: data.length === options.limit,
+ };
+};
diff --git a/src/utils/relational-entity-helper.ts b/src/utils/relational-entity-helper.ts
new file mode 100644
index 0000000..ee0e179
--- /dev/null
+++ b/src/utils/relational-entity-helper.ts
@@ -0,0 +1,15 @@
+import { instanceToPlain } from 'class-transformer';
+import { AfterLoad, BaseEntity } from 'typeorm';
+
+export class EntityRelationalHelper extends BaseEntity {
+ __entity?: string;
+
+ @AfterLoad()
+ setEntityName() {
+ this.__entity = this.constructor.name;
+ }
+
+ toJSON() {
+ return instanceToPlain(this);
+ }
+}
diff --git a/src/utils/serializer.interceptor.ts b/src/utils/serializer.interceptor.ts
new file mode 100644
index 0000000..93f45de
--- /dev/null
+++ b/src/utils/serializer.interceptor.ts
@@ -0,0 +1,16 @@
+import {
+ Injectable,
+ NestInterceptor,
+ ExecutionContext,
+ CallHandler,
+} from '@nestjs/common';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import deepResolvePromises from './deep-resolver';
+
+@Injectable()
+export class ResolvePromisesInterceptor implements NestInterceptor {
+ intercept(context: ExecutionContext, next: CallHandler): Observable {
+ return next.handle().pipe(map((data) => deepResolvePromises(data)));
+ }
+}
diff --git a/src/utils/transformers/lower-case.transformer.ts b/src/utils/transformers/lower-case.transformer.ts
new file mode 100644
index 0000000..787d11b
--- /dev/null
+++ b/src/utils/transformers/lower-case.transformer.ts
@@ -0,0 +1,6 @@
+import { TransformFnParams } from 'class-transformer/types/interfaces';
+import { MaybeType } from '../types/maybe.type';
+
+export const lowerCaseTransformer = (
+ params: TransformFnParams,
+): MaybeType => params.value?.toLowerCase().trim();
diff --git a/src/utils/types/deep-partial.type.ts b/src/utils/types/deep-partial.type.ts
new file mode 100644
index 0000000..4fff188
--- /dev/null
+++ b/src/utils/types/deep-partial.type.ts
@@ -0,0 +1,3 @@
+export type DeepPartial = {
+ [P in keyof T]?: DeepPartial;
+};
diff --git a/src/utils/types/entity-condition.type.ts b/src/utils/types/entity-condition.type.ts
new file mode 100644
index 0000000..4adb67c
--- /dev/null
+++ b/src/utils/types/entity-condition.type.ts
@@ -0,0 +1,3 @@
+export type EntityCondition = {
+ [P in keyof T]?: T[P] | T[P][] | undefined;
+};
diff --git a/src/utils/types/maybe.type.ts b/src/utils/types/maybe.type.ts
new file mode 100644
index 0000000..7e5efa5
--- /dev/null
+++ b/src/utils/types/maybe.type.ts
@@ -0,0 +1 @@
+export type MaybeType = T | undefined;
diff --git a/src/utils/types/nullable.type.ts b/src/utils/types/nullable.type.ts
new file mode 100644
index 0000000..09c14f4
--- /dev/null
+++ b/src/utils/types/nullable.type.ts
@@ -0,0 +1 @@
+export type NullableType = T | null;
diff --git a/src/utils/types/or-never.type.ts b/src/utils/types/or-never.type.ts
new file mode 100644
index 0000000..cafa1dd
--- /dev/null
+++ b/src/utils/types/or-never.type.ts
@@ -0,0 +1 @@
+export type OrNeverType = T | never;
diff --git a/src/utils/types/pagination-options.ts b/src/utils/types/pagination-options.ts
new file mode 100644
index 0000000..7616a0b
--- /dev/null
+++ b/src/utils/types/pagination-options.ts
@@ -0,0 +1,4 @@
+export interface IPaginationOptions {
+ page: number;
+ limit: number;
+}
diff --git a/src/utils/validate-config.ts b/src/utils/validate-config.ts
new file mode 100644
index 0000000..5738de2
--- /dev/null
+++ b/src/utils/validate-config.ts
@@ -0,0 +1,22 @@
+import { plainToClass } from 'class-transformer';
+import { validateSync } from 'class-validator';
+import { ClassConstructor } from 'class-transformer/types/interfaces';
+
+function validateConfig(
+ config: Record,
+ envVariablesClass: ClassConstructor,
+) {
+ const validatedConfig = plainToClass(envVariablesClass, config, {
+ enableImplicitConversion: true,
+ });
+ const errors = validateSync(validatedConfig, {
+ skipMissingProperties: false,
+ });
+
+ if (errors.length > 0) {
+ throw new Error(errors.toString());
+ }
+ return validatedConfig;
+}
+
+export default validateConfig;
diff --git a/src/utils/validation-options.ts b/src/utils/validation-options.ts
new file mode 100644
index 0000000..7ff58fa
--- /dev/null
+++ b/src/utils/validation-options.ts
@@ -0,0 +1,33 @@
+import {
+ HttpStatus,
+ UnprocessableEntityException,
+ ValidationError,
+ ValidationPipeOptions,
+} from '@nestjs/common';
+
+function generateErrors(errors: ValidationError[]) {
+ return errors.reduce(
+ (accumulator, currentValue) => ({
+ ...accumulator,
+ [currentValue.property]:
+ (currentValue.children?.length ?? 0) > 0
+ ? generateErrors(currentValue.children ?? [])
+ : Object.values(currentValue.constraints ?? {}).join(', '),
+ }),
+ {},
+ );
+}
+
+const validationOptions: ValidationPipeOptions = {
+ transform: true,
+ whitelist: true,
+ errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
+ exceptionFactory: (errors: ValidationError[]) => {
+ return new UnprocessableEntityException({
+ status: HttpStatus.UNPROCESSABLE_ENTITY,
+ errors: generateErrors(errors),
+ });
+ },
+};
+
+export default validationOptions;
diff --git a/startup.document.ci.sh b/startup.document.ci.sh
new file mode 100755
index 0000000..b77ba28
--- /dev/null
+++ b/startup.document.ci.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -e
+
+/opt/wait-for-it.sh mongo:27017
+npm run seed:run:document
+npm run start:prod > prod.log 2>&1 &
+/opt/wait-for-it.sh maildev:1080
+/opt/wait-for-it.sh localhost:3000
+npm run lint
+npm run test:e2e -- --runInBand
diff --git a/startup.document.dev.sh b/startup.document.dev.sh
new file mode 100755
index 0000000..fdacb07
--- /dev/null
+++ b/startup.document.dev.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -e
+
+/opt/wait-for-it.sh mongo:27017
+cat .env
+npm run seed:run:document
+npm run start:prod
diff --git a/startup.document.test.sh b/startup.document.test.sh
new file mode 100755
index 0000000..2d8cca6
--- /dev/null
+++ b/startup.document.test.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -e
+
+/opt/wait-for-it.sh mongo:27017
+/opt/wait-for-it.sh maildev:1080
+npm install
+npm run seed:run:document
+npm run start:swc
diff --git a/startup.relational.ci.sh b/startup.relational.ci.sh
new file mode 100755
index 0000000..37f659b
--- /dev/null
+++ b/startup.relational.ci.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+set -e
+
+/opt/wait-for-it.sh postgres:5432
+npm run migration:run
+npm run seed:run:relational
+npm run start:prod > prod.log 2>&1 &
+/opt/wait-for-it.sh maildev:1080
+/opt/wait-for-it.sh localhost:3000
+npm run lint
+npm run test:e2e -- --runInBand
diff --git a/startup.relational.dev.sh b/startup.relational.dev.sh
new file mode 100755
index 0000000..1d2d050
--- /dev/null
+++ b/startup.relational.dev.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -e
+
+/opt/wait-for-it.sh postgres:5432
+npm run migration:run
+npm run seed:run:relational
+npm run start:prod
diff --git a/startup.relational.test.sh b/startup.relational.test.sh
new file mode 100755
index 0000000..6e56758
--- /dev/null
+++ b/startup.relational.test.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -e
+
+/opt/wait-for-it.sh postgres:5432
+/opt/wait-for-it.sh maildev:1080
+npm install
+npm run migration:run
+npm run seed:run:relational
+npm run start:swc
diff --git a/test/admin/auth.e2e-spec.ts b/test/admin/auth.e2e-spec.ts
new file mode 100644
index 0000000..d99e25c
--- /dev/null
+++ b/test/admin/auth.e2e-spec.ts
@@ -0,0 +1,20 @@
+import request from 'supertest';
+import { ADMIN_EMAIL, ADMIN_PASSWORD, APP_URL } from '../utils/constants';
+
+describe('Auth', () => {
+ const app = APP_URL;
+
+ describe('Admin', () => {
+ it('should successfully login via /api/v1/auth/email/login (POST)', () => {
+ return request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: ADMIN_EMAIL, password: ADMIN_PASSWORD })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ expect(body.user.email).toBeDefined();
+ expect(body.user.role).toBeDefined();
+ });
+ });
+ });
+});
diff --git a/test/admin/users.e2e-spec.ts b/test/admin/users.e2e-spec.ts
new file mode 100644
index 0000000..29ddab8
--- /dev/null
+++ b/test/admin/users.e2e-spec.ts
@@ -0,0 +1,148 @@
+import { APP_URL, ADMIN_EMAIL, ADMIN_PASSWORD } from '../utils/constants';
+import request from 'supertest';
+import { RoleEnum } from '../../src/roles/roles.enum';
+import { StatusEnum } from '../../src/statuses/statuses.enum';
+
+describe('Users Module', () => {
+ const app = APP_URL;
+ let apiToken;
+
+ beforeAll(async () => {
+ await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: ADMIN_EMAIL, password: ADMIN_PASSWORD })
+ .then(({ body }) => {
+ apiToken = body.token;
+ });
+ });
+
+ describe('Update', () => {
+ let newUser;
+ const newUserEmail = `user-first.${Date.now()}@example.com`;
+ const newUserChangedEmail = `user-first-changed.${Date.now()}@example.com`;
+ const newUserPassword = `secret`;
+ const newUserChangedPassword = `new-secret`;
+
+ beforeAll(async () => {
+ await request(app)
+ .post('/api/v1/auth/email/register')
+ .send({
+ email: newUserEmail,
+ password: newUserPassword,
+ firstName: `First${Date.now()}`,
+ lastName: 'E2E',
+ });
+
+ await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => {
+ newUser = body.user;
+ });
+ });
+
+ describe('User with "Admin" role', () => {
+ it('should change password for existing user: /api/v1/users/:id (PATCH)', () => {
+ return request(app)
+ .patch(`/api/v1/users/${newUser.id}`)
+ .auth(apiToken, {
+ type: 'bearer',
+ })
+ .send({
+ email: newUserChangedEmail,
+ password: newUserChangedPassword,
+ })
+ .expect(200);
+ });
+
+ describe('Guest', () => {
+ it('should login with changed password: /api/v1/auth/email/login (POST)', () => {
+ return request(app)
+ .post('/api/v1/auth/email/login')
+ .send({
+ email: newUserChangedEmail,
+ password: newUserChangedPassword,
+ })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ });
+ });
+ });
+ });
+ });
+
+ describe('Create', () => {
+ const newUserByAdminEmail = `user-created-by-admin.${Date.now()}@example.com`;
+ const newUserByAdminPassword = `secret`;
+
+ describe('User with "Admin" role', () => {
+ it('should fail to create new user with invalid email: /api/v1/users (POST)', () => {
+ return request(app)
+ .post(`/api/v1/users`)
+ .auth(apiToken, {
+ type: 'bearer',
+ })
+ .send({ email: 'fail-data' })
+ .expect(422);
+ });
+
+ it('should successfully create new user: /api/v1/users (POST)', () => {
+ return request(app)
+ .post(`/api/v1/users`)
+ .auth(apiToken, {
+ type: 'bearer',
+ })
+ .send({
+ email: newUserByAdminEmail,
+ password: newUserByAdminPassword,
+ firstName: `UserByAdmin${Date.now()}`,
+ lastName: 'E2E',
+ role: {
+ id: RoleEnum.user,
+ },
+ status: {
+ id: StatusEnum.active,
+ },
+ })
+ .expect(201);
+ });
+
+ describe('Guest', () => {
+ it('should successfully login via created by admin user: /api/v1/auth/email/login (GET)', () => {
+ return request(app)
+ .post('/api/v1/auth/email/login')
+ .send({
+ email: newUserByAdminEmail,
+ password: newUserByAdminPassword,
+ })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ });
+ });
+ });
+ });
+ });
+
+ describe('Get many', () => {
+ describe('User with "Admin" role', () => {
+ it('should get list of users: /api/v1/users (GET)', () => {
+ return request(app)
+ .get(`/api/v1/users`)
+ .auth(apiToken, {
+ type: 'bearer',
+ })
+ .expect(200)
+ .send()
+ .expect(({ body }) => {
+ expect(body.data[0].provider).toBeDefined();
+ expect(body.data[0].email).toBeDefined();
+ expect(body.data[0].hash).not.toBeDefined();
+ expect(body.data[0].password).not.toBeDefined();
+ expect(body.data[0].previousPassword).not.toBeDefined();
+ });
+ });
+ });
+ });
+});
diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts
deleted file mode 100644
index 50cda62..0000000
--- a/test/app.e2e-spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { INestApplication } from '@nestjs/common';
-import * as request from 'supertest';
-import { AppModule } from './../src/app.module';
-
-describe('AppController (e2e)', () => {
- let app: INestApplication;
-
- beforeEach(async () => {
- const moduleFixture: TestingModule = await Test.createTestingModule({
- imports: [AppModule],
- }).compile();
-
- app = moduleFixture.createNestApplication();
- await app.init();
- });
-
- it('/ (GET)', () => {
- return request(app.getHttpServer())
- .get('/')
- .expect(200)
- .expect('Hello World!');
- });
-});
diff --git a/test/user/auth.e2e-spec.ts b/test/user/auth.e2e-spec.ts
new file mode 100644
index 0000000..1fdace5
--- /dev/null
+++ b/test/user/auth.e2e-spec.ts
@@ -0,0 +1,348 @@
+import request from 'supertest';
+import {
+ APP_URL,
+ TESTER_EMAIL,
+ TESTER_PASSWORD,
+ MAIL_HOST,
+ MAIL_PORT,
+} from '../utils/constants';
+
+describe('Auth Module', () => {
+ const app = APP_URL;
+ const mail = `http://${MAIL_HOST}:${MAIL_PORT}`;
+ const newUserFirstName = `Tester${Date.now()}`;
+ const newUserLastName = `E2E`;
+ const newUserEmail = `User.${Date.now()}@example.com`;
+ const newUserPassword = `secret`;
+
+ describe('Registration', () => {
+ it('should fail with exists email: /api/v1/auth/email/register (POST)', () => {
+ return request(app)
+ .post('/api/v1/auth/email/register')
+ .send({
+ email: TESTER_EMAIL,
+ password: TESTER_PASSWORD,
+ firstName: 'Tester',
+ lastName: 'E2E',
+ })
+ .expect(422)
+ .expect(({ body }) => {
+ expect(body.errors.email).toBeDefined();
+ });
+ });
+
+ it('should successfully: /api/v1/auth/email/register (POST)', async () => {
+ return request(app)
+ .post('/api/v1/auth/email/register')
+ .send({
+ email: newUserEmail,
+ password: newUserPassword,
+ firstName: newUserFirstName,
+ lastName: newUserLastName,
+ })
+ .expect(204);
+ });
+
+ describe('Login', () => {
+ it('should successfully with unconfirmed email: /api/v1/auth/email/login (POST)', () => {
+ return request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ });
+ });
+ });
+
+ describe('Confirm email', () => {
+ it('should successfully: /api/v1/auth/email/confirm (POST)', async () => {
+ const hash = await request(mail)
+ .get('/email')
+ .then(({ body }) =>
+ body
+ .find(
+ (letter) =>
+ letter.to[0].address.toLowerCase() ===
+ newUserEmail.toLowerCase() &&
+ /.*confirm\-email\?hash\=(\S+).*/g.test(letter.text),
+ )
+ ?.text.replace(/.*confirm\-email\?hash\=(\S+).*/g, '$1'),
+ );
+
+ return request(app)
+ .post('/api/v1/auth/email/confirm')
+ .send({
+ hash,
+ })
+ .expect(204);
+ });
+
+ it('should fail for already confirmed email: /api/v1/auth/email/confirm (POST)', async () => {
+ const hash = await request(mail)
+ .get('/email')
+ .then(({ body }) =>
+ body
+ .find(
+ (letter) =>
+ letter.to[0].address.toLowerCase() ===
+ newUserEmail.toLowerCase() &&
+ /.*confirm\-email\?hash\=(\S+).*/g.test(letter.text),
+ )
+ ?.text.replace(/.*confirm\-email\?hash\=(\S+).*/g, '$1'),
+ );
+
+ return request(app)
+ .post('/api/v1/auth/email/confirm')
+ .send({
+ hash,
+ })
+ .expect(404);
+ });
+ });
+ });
+
+ describe('Login', () => {
+ it('should successfully for user with confirmed email: /api/v1/auth/email/login (POST)', () => {
+ return request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ expect(body.refreshToken).toBeDefined();
+ expect(body.tokenExpires).toBeDefined();
+ expect(body.user.email).toBeDefined();
+ expect(body.user.hash).not.toBeDefined();
+ expect(body.user.password).not.toBeDefined();
+ expect(body.user.previousPassword).not.toBeDefined();
+ });
+ });
+ });
+
+ describe('Logged in user', () => {
+ let newUserApiToken;
+
+ beforeAll(async () => {
+ await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => {
+ newUserApiToken = body.token;
+ });
+ });
+
+ it('should retrieve your own profile: /api/v1/auth/me (GET)', async () => {
+ await request(app)
+ .get('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .send()
+ .expect(({ body }) => {
+ expect(body.provider).toBeDefined();
+ expect(body.email).toBeDefined();
+ expect(body.hash).not.toBeDefined();
+ expect(body.password).not.toBeDefined();
+ expect(body.previousPassword).not.toBeDefined();
+ });
+ });
+
+ it('should get new refresh token: /api/v1/auth/refresh (POST)', async () => {
+ let newUserRefreshToken = await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => body.refreshToken);
+
+ newUserRefreshToken = await request(app)
+ .post('/api/v1/auth/refresh')
+ .auth(newUserRefreshToken, {
+ type: 'bearer',
+ })
+ .send()
+ .then(({ body }) => body.refreshToken);
+
+ await request(app)
+ .post('/api/v1/auth/refresh')
+ .auth(newUserRefreshToken, {
+ type: 'bearer',
+ })
+ .send()
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ expect(body.refreshToken).toBeDefined();
+ expect(body.tokenExpires).toBeDefined();
+ });
+ });
+
+ it('should fail on the second attempt to refresh token with the same token: /api/v1/auth/refresh (POST)', async () => {
+ const newUserRefreshToken = await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => body.refreshToken);
+
+ await request(app)
+ .post('/api/v1/auth/refresh')
+ .auth(newUserRefreshToken, {
+ type: 'bearer',
+ })
+ .send();
+
+ await request(app)
+ .post('/api/v1/auth/refresh')
+ .auth(newUserRefreshToken, {
+ type: 'bearer',
+ })
+ .send()
+ .expect(401);
+ });
+
+ it('should update profile successfully: /api/v1/auth/me (PATCH)', async () => {
+ const newUserNewName = Date.now();
+ const newUserNewPassword = 'new-secret';
+ const newUserApiToken = await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => body.token);
+
+ await request(app)
+ .patch('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .send({
+ firstName: newUserNewName,
+ password: newUserNewPassword,
+ })
+ .expect(422);
+
+ await request(app)
+ .patch('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .send({
+ firstName: newUserNewName,
+ password: newUserNewPassword,
+ oldPassword: newUserPassword,
+ })
+ .expect(200);
+
+ await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserNewPassword })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.token).toBeDefined();
+ });
+
+ await request(app)
+ .patch('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .send({ password: newUserPassword, oldPassword: newUserNewPassword })
+ .expect(200);
+ });
+
+ it('should update profile email successfully: /api/v1/auth/me (PATCH)', async () => {
+ const newUserFirstName = `Tester${Date.now()}`;
+ const newUserLastName = `E2E`;
+ const newUserEmail = `user.${Date.now()}@example.com`;
+ const newUserPassword = `secret`;
+ const newUserNewEmail = `new.${newUserEmail}`;
+
+ await request(app)
+ .post('/api/v1/auth/email/register')
+ .send({
+ email: newUserEmail,
+ password: newUserPassword,
+ firstName: newUserFirstName,
+ lastName: newUserLastName,
+ })
+ .expect(204);
+
+ const newUserApiToken = await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => body.token);
+
+ await request(app)
+ .patch('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .send({
+ email: newUserNewEmail,
+ })
+ .expect(200);
+
+ const hash = await request(mail)
+ .get('/email')
+ .then(({ body }) =>
+ body
+ .find((letter) => {
+ return (
+ letter.to[0].address.toLowerCase() ===
+ newUserNewEmail.toLowerCase() &&
+ /.*confirm\-new\-email\?hash\=(\S+).*/g.test(letter.text)
+ );
+ })
+ ?.text.replace(/.*confirm\-new\-email\?hash\=(\S+).*/g, '$1'),
+ );
+
+ await request(app)
+ .get('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.email).not.toBe(newUserNewEmail);
+ });
+
+ await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserNewEmail, password: newUserPassword })
+ .expect(422);
+
+ await request(app)
+ .post('/api/v1/auth/email/confirm/new')
+ .send({
+ hash,
+ })
+ .expect(204);
+
+ await request(app)
+ .get('/api/v1/auth/me')
+ .auth(newUserApiToken, {
+ type: 'bearer',
+ })
+ .expect(200)
+ .expect(({ body }) => {
+ expect(body.email).toBe(newUserNewEmail);
+ });
+
+ await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserNewEmail, password: newUserPassword })
+ .expect(200);
+ });
+
+ it('should delete profile successfully: /api/v1/auth/me (DELETE)', async () => {
+ const newUserApiToken = await request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .then(({ body }) => body.token);
+
+ await request(app).delete('/api/v1/auth/me').auth(newUserApiToken, {
+ type: 'bearer',
+ });
+
+ return request(app)
+ .post('/api/v1/auth/email/login')
+ .send({ email: newUserEmail, password: newUserPassword })
+ .expect(422);
+ });
+ });
+});
diff --git a/test/utils/constants.ts b/test/utils/constants.ts
new file mode 100644
index 0000000..b1bfb95
--- /dev/null
+++ b/test/utils/constants.ts
@@ -0,0 +1,7 @@
+export const APP_URL = `http://localhost:${process.env.APP_PORT}`;
+export const TESTER_EMAIL = 'john.doe@example.com';
+export const TESTER_PASSWORD = 'secret';
+export const ADMIN_EMAIL = 'admin@example.com';
+export const ADMIN_PASSWORD = 'secret';
+export const MAIL_HOST = process.env.MAIL_HOST;
+export const MAIL_PORT = process.env.MAIL_CLIENT_PORT;
diff --git a/tsconfig.json b/tsconfig.json
index bf10a23..4da464e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,10 +6,17 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
- "target": "es2017",
+ "target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
- "incremental": true
+ "incremental": true,
+ "skipLibCheck": true,
+ "strictNullChecks": true,
+ "noImplicitAny": false,
+ "strictBindCallApply": false,
+ "forceConsistentCasingInFileNames": false,
+ "noFallthroughCasesInSwitch": false,
+ "esModuleInterop": true
}
}
diff --git a/wait-for-it.sh b/wait-for-it.sh
new file mode 100755
index 0000000..d990e0d
--- /dev/null
+++ b/wait-for-it.sh
@@ -0,0 +1,182 @@
+#!/usr/bin/env bash
+# Use this script to test if a given TCP host/port are available
+
+WAITFORIT_cmdname=${0##*/}
+
+echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
+
+usage()
+{
+ cat << USAGE >&2
+Usage:
+ $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
+ -h HOST | --host=HOST Host or IP under test
+ -p PORT | --port=PORT TCP port under test
+ Alternatively, you specify the host and port as host:port
+ -s | --strict Only execute subcommand if the test succeeds
+ -q | --quiet Don't output any status messages
+ -t TIMEOUT | --timeout=TIMEOUT
+ Timeout in seconds, zero for no timeout
+ -- COMMAND ARGS Execute command with args after the test finishes
+USAGE
+ exit 1
+}
+
+wait_for()
+{
+ if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+ echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+ else
+ echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
+ fi
+ WAITFORIT_start_ts=$(date +%s)
+ while :
+ do
+ if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
+ nc -z $WAITFORIT_HOST $WAITFORIT_PORT
+ WAITFORIT_result=$?
+ else
+ (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
+ WAITFORIT_result=$?
+ fi
+ if [[ $WAITFORIT_result -eq 0 ]]; then
+ WAITFORIT_end_ts=$(date +%s)
+ echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
+ break
+ fi
+ sleep 1
+ done
+ return $WAITFORIT_result
+}
+
+wait_for_wrapper()
+{
+ # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
+ if [[ $WAITFORIT_QUIET -eq 1 ]]; then
+ timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+ else
+ timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+ fi
+ WAITFORIT_PID=$!
+ trap "kill -INT -$WAITFORIT_PID" INT
+ wait $WAITFORIT_PID
+ WAITFORIT_RESULT=$?
+ if [[ $WAITFORIT_RESULT -ne 0 ]]; then
+ echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+ fi
+ return $WAITFORIT_RESULT
+}
+
+# process arguments
+while [[ $# -gt 0 ]]
+do
+ case "$1" in
+ *:* )
+ WAITFORIT_hostport=(${1//:/ })
+ WAITFORIT_HOST=${WAITFORIT_hostport[0]}
+ WAITFORIT_PORT=${WAITFORIT_hostport[1]}
+ shift 1
+ ;;
+ --child)
+ WAITFORIT_CHILD=1
+ shift 1
+ ;;
+ -q | --quiet)
+ WAITFORIT_QUIET=1
+ shift 1
+ ;;
+ -s | --strict)
+ WAITFORIT_STRICT=1
+ shift 1
+ ;;
+ -h)
+ WAITFORIT_HOST="$2"
+ if [[ $WAITFORIT_HOST == "" ]]; then break; fi
+ shift 2
+ ;;
+ --host=*)
+ WAITFORIT_HOST="${1#*=}"
+ shift 1
+ ;;
+ -p)
+ WAITFORIT_PORT="$2"
+ if [[ $WAITFORIT_PORT == "" ]]; then break; fi
+ shift 2
+ ;;
+ --port=*)
+ WAITFORIT_PORT="${1#*=}"
+ shift 1
+ ;;
+ -t)
+ WAITFORIT_TIMEOUT="$2"
+ if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
+ shift 2
+ ;;
+ --timeout=*)
+ WAITFORIT_TIMEOUT="${1#*=}"
+ shift 1
+ ;;
+ --)
+ shift
+ WAITFORIT_CLI=("$@")
+ break
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ echoerr "Unknown argument: $1"
+ usage
+ ;;
+ esac
+done
+
+if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
+ echoerr "Error: you need to provide a host and port to test."
+ usage
+fi
+
+WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
+WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
+WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
+WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
+
+# Check to see if timeout is from busybox?
+WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
+WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
+
+WAITFORIT_BUSYTIMEFLAG=""
+if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
+ WAITFORIT_ISBUSY=1
+ # Check if busybox timeout uses -t flag
+ # (recent Alpine versions don't support -t anymore)
+ if timeout &>/dev/stdout | grep -q -e '-t '; then
+ WAITFORIT_BUSYTIMEFLAG="-t"
+ fi
+else
+ WAITFORIT_ISBUSY=0
+fi
+
+if [[ $WAITFORIT_CHILD -gt 0 ]]; then
+ wait_for
+ WAITFORIT_RESULT=$?
+ exit $WAITFORIT_RESULT
+else
+ if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+ wait_for_wrapper
+ WAITFORIT_RESULT=$?
+ else
+ wait_for
+ WAITFORIT_RESULT=$?
+ fi
+fi
+
+if [[ $WAITFORIT_CLI != "" ]]; then
+ if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
+ echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
+ exit $WAITFORIT_RESULT
+ fi
+ exec "${WAITFORIT_CLI[@]}"
+else
+ exit $WAITFORIT_RESULT
+fi