From f33f679e124077fa1148264fe44f234c54a475f8 Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 4 Jul 2023 12:16:34 +0200 Subject: [PATCH 01/23] WIP handle unique constraint exception --- package-lock.json | 8764 +++-------------- package.json | 2 + .../migration.sql | 7 +- prisma/schema.prisma | 12 +- src/app.module.ts | 4 +- src/main.ts | 2 +- .../secondaries/prisma-repository.abstract.ts | 4 +- .../authentication.di-tokens.ts | 1 + .../authentication.mapper.ts | 66 + .../authentication.module.ts | 31 + .../create-authentication.command.ts | 13 + .../create-authentication.service.ts | 47 + .../ports/authentication.repository.port.ts | 4 + .../application/commands/types/username.ts | 6 + .../core/domain/authentication.entity.ts | 35 + .../core/domain/authentication.errors.ts | 21 + .../core/domain/authentication.types.ts | 13 + .../authentication-created.domain-events.ts | 7 + .../core/domain/username.entity.ts | 9 + .../core/domain/username.types.ts | 16 + .../authentication.repository.ts | 56 + .../infrastructure/prisma.service.ts | 15 + .../authentication.paginated.response.dto.ts | 6 + .../dtos/authentication.response.dto.ts | 3 + .../grpc-controllers/authentication.proto | 40 + .../create-authentication.grpc.controller.ts | 43 + .../dtos/create-authentication.request.dto.ts | 21 + .../grpc-controllers/dtos/username.dto.ts | 11 + tsconfig.json | 6 +- 29 files changed, 1608 insertions(+), 7657 deletions(-) rename prisma/migrations/{20221220135616_init => 20230704081950_init}/migration.sql (68%) create mode 100644 src/modules/newauthentication/authentication.di-tokens.ts create mode 100644 src/modules/newauthentication/authentication.mapper.ts create mode 100644 src/modules/newauthentication/authentication.module.ts create mode 100644 src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts create mode 100644 src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts create mode 100644 src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts create mode 100644 src/modules/newauthentication/core/application/commands/types/username.ts create mode 100644 src/modules/newauthentication/core/domain/authentication.entity.ts create mode 100644 src/modules/newauthentication/core/domain/authentication.errors.ts create mode 100644 src/modules/newauthentication/core/domain/authentication.types.ts create mode 100644 src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts create mode 100644 src/modules/newauthentication/core/domain/username.entity.ts create mode 100644 src/modules/newauthentication/core/domain/username.types.ts create mode 100644 src/modules/newauthentication/infrastructure/authentication.repository.ts create mode 100644 src/modules/newauthentication/infrastructure/prisma.service.ts create mode 100644 src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts create mode 100644 src/modules/newauthentication/interface/dtos/authentication.response.dto.ts create mode 100644 src/modules/newauthentication/interface/grpc-controllers/authentication.proto create mode 100644 src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts create mode 100644 src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts create mode 100644 src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts diff --git a/package-lock.json b/package-lock.json index 7fa7d67..7802456 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "@mobicoop/auth", "version": "0.0.1", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -15,11 +15,13 @@ "@golevelup/nestjs-rabbitmq": "^3.4.0", "@grpc/grpc-js": "^1.8.0", "@grpc/proto-loader": "^0.7.4", + "@mobicoop/ddd-library": "file:../../packages/dddlibrary", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.2.0", "@nestjs/core": "^9.0.0", "@nestjs/cqrs": "^9.0.1", + "@nestjs/event-emitter": "^2.0.0", "@nestjs/microservices": "^9.2.1", "@nestjs/platform-express": "^9.0.0", "@nestjs/terminus": "^9.2.2", @@ -61,13 +63,22 @@ "uuid": "^9.0.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -75,19 +86,19 @@ } }, "node_modules/@angular-devkit/core": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz", - "integrity": "sha512-yl+0j1bMwJLKShsyCXw77tbJG8Sd21+itisPLL2MgEpLNAO252kr9zG4TLlFRJyKVftm2l1h78KjqvM5nbOXNg==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.1.tgz", + "integrity": "sha512-2uz98IqkKJlgnHbWQ7VeL4pb+snGAZXIama2KXi+k9GsRntdcw+udX8rL3G9SdUGUF+m6+147Y1oRBMHsO/v4w==", "dev": true, "dependencies": { "ajv": "8.12.0", "ajv-formats": "2.1.1", "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", + "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "node": "^16.14.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -100,50 +111,32 @@ } } }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@angular-devkit/schematics": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.4.tgz", - "integrity": "sha512-/W7/vvn59PAVLzhcvD4/N/E8RDhub8ny1A7I96LTRjC5o+yvVV16YJ4YJzolrRrIEN01KmLVQJ9A58VCaweMgw==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.0.1.tgz", + "integrity": "sha512-A9D0LTYmiqiBa90GKcSuWb7hUouGIbm/AHbJbjL85WLLRbQA2PwKl7P5Mpd6nS/ZC0kfG4VQY3VOaDvb3qpI9g==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", + "@angular-devkit/core": "16.0.1", "jsonc-parser": "3.2.0", - "magic-string": "0.29.0", + "magic-string": "0.30.0", "ora": "5.4.1", - "rxjs": "6.6.7" + "rxjs": "7.8.1" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "node": "^16.14.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular-devkit/schematics-cli": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-15.2.4.tgz", - "integrity": "sha512-QTTKEH5HOkxvQtCxb2Lna2wubehkaIzA6DKUBISijPQliLomw74tzc7lXCywmMqRTbQPVRLG3kBK97hR4x67nA==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-16.0.1.tgz", + "integrity": "sha512-6KLA125dpgd6oJGtiO2JpZAb92uOG3njQGIt7NFcuQGW/5GO7J41vMXH9cBAfdtbV8SIggSmR/cIEE9ijfj6YQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", "ansi-colors": "4.1.3", "inquirer": "8.2.4", "symbol-observable": "4.0.0", @@ -153,7 +146,7 @@ "schematics": "bin/schematics.js" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "node": "^16.14.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -184,24 +177,6 @@ "node": ">=12.0.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@automapper/classes": { "version": "8.7.7", "resolved": "https://registry.npmjs.org/@automapper/classes/-/classes-8.7.7.tgz", @@ -236,47 +211,47 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", + "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", - "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.6.tgz", + "integrity": "sha512-HPIyDa6n+HKw5dEuway3vVAhBboYCtREBMp+IWeseZy6TFtzn6MHkCH2KKYUOC/vKKwgSMHQW4htBOrmuRPXfw==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-module-transforms": "^7.20.2", - "@babel/helpers": "^7.20.5", - "@babel/parser": "^7.20.5", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.6", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.2" }, "engines": { "node": ">=6.9.0" @@ -286,53 +261,32 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.5", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", - "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", + "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" + "@babel/compat-data": "^7.22.6", + "@babel/helper-validator-option": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1" }, "engines": { "node": ">=6.9.0" @@ -341,161 +295,152 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", - "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", - "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -575,9 +520,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", + "integrity": "sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -734,12 +679,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -749,33 +694,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.6.tgz", + "integrity": "sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.6", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -793,13 +738,13 @@ } }, "node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -844,16 +789,40 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -889,20 +858,37 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/@eslint/js": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@golevelup/nestjs-common": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-common/-/nestjs-common-1.4.4.tgz", - "integrity": "sha512-NTjtOhHTMuGwiR3lmBQKKaRr++mHQEsh8AxtaH+/EWOYKMK2Cv/8duaH9MQ0hI3TwnouyaA5IRxYR1ZCUyNXOQ==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@golevelup/nestjs-common/-/nestjs-common-1.4.5.tgz", + "integrity": "sha512-WqxGAP4KZjvUea/lYCEfFXB8fS3NwxEWgrQpz2H8jusbz11OSrLECv5TNU9RIRKFnUPbGevy45dvKHF2JTQfJw==", "dependencies": { + "lodash": "^4.17.21", "nanoid": "^3.2.0" + }, + "peerDependencies": { + "@nestjs/common": "^9.x" } }, "node_modules/@golevelup/nestjs-discovery": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-3.0.0.tgz", - "integrity": "sha512-ZvkXtobTKxXB1LJanP/l6Z/Fing88IMBr3uabQpU2IWjfsstjh02qYDSU2cfD6CSmNldX5ewW5Pd+SdK2lU8Sw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-3.0.1.tgz", + "integrity": "sha512-kK/GBYVxb8XGlwXgtCWAkPOwDVh7dXyLRaoZuk2bBYntV3DZkYGAIbLKOFTGz+MGz71vEeQ9bGLP7cHKtCee4g==", "dependencies": { "lodash": "^4.17.15" + }, + "peerDependencies": { + "@nestjs/common": "^9.x", + "@nestjs/core": "^9.x" } }, "node_modules/@golevelup/nestjs-modules": { @@ -918,21 +904,28 @@ } }, "node_modules/@golevelup/nestjs-rabbitmq": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-rabbitmq/-/nestjs-rabbitmq-3.4.0.tgz", - "integrity": "sha512-Ca6JtboZL11qTWl8mFYT3phs+JAhZDhct6/gKbcNEhaaEd3IU6+hEBnjs7LDbYSAIdlpZNODy/rRGSPCFmUYsQ==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@golevelup/nestjs-rabbitmq/-/nestjs-rabbitmq-3.7.0.tgz", + "integrity": "sha512-wV6SA8oCTu8v66U5i3vu/k6DEvlHKM+LTb9JY7Iz8C0nUUuf6pufgvAalusAi4iJJ7/7p3Q44LF38SB4buszWw==", "dependencies": { - "@golevelup/nestjs-common": "^1.4.4", - "@golevelup/nestjs-discovery": "^3.0.0", + "@golevelup/nestjs-common": "^1.4.5", + "@golevelup/nestjs-discovery": "^3.0.1", "@golevelup/nestjs-modules": "^0.6.1", "amqp-connection-manager": "^3.0.0", - "amqplib": "^0.8.0" + "amqplib": "^0.8.0", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^9.x", + "@nestjs/core": "^9.x", + "reflect-metadata": "^0.1.0", + "rxjs": "^7.x" } }, "node_modules/@grpc/grpc-js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.0.tgz", - "integrity": "sha512-ySMTXQuMvvswoobvN+0LsaPf7ITO2JVfJmHxQKI4cGehNrrUms+n81BlHEX7Hl/LExji6XE3fnI9U04GSkRruA==", + "version": "1.8.17", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.17.tgz", + "integrity": "sha512-DGuSbtMFbaRsyffMf+VEkVu8HkSXEUfO3UyGJNtqxW9ABdtTIA+2UXAJpwbJS+xfQxuwqLUeELmL6FuZkOqPxw==", "dependencies": { "@grpc/proto-loader": "^0.7.0", "@types/node": ">=12.12.47" @@ -942,15 +935,15 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.4.tgz", - "integrity": "sha512-MnWjkGwqQ3W8fx94/c1CwqLsNmHHv2t0CFn+9++6+cDphC1lolpg9M2OU0iebIjK//pBNX9e94ho+gjx6vz39w==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", + "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", "dependencies": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", "long": "^4.0.0", "protobufjs": "^7.0.0", - "yargs": "^16.2.0" + "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" @@ -959,45 +952,10 @@ "node": ">=6" } }, - "node_modules/@grpc/proto-loader/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/@grpc/proto-loader/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1052,6 +1010,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1200,51 +1167,6 @@ } } }, - "node_modules/@jest/core/node_modules/jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, "node_modules/@jest/environment": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", @@ -1460,13 +1382,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -1491,43 +1414,43 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, - "node_modules/@jridgewell/sourcemap-codec": { + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" } }, "node_modules/@mapbox/node-pre-gyp": { @@ -1549,6 +1472,34 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@mobicoop/ddd-library": { + "version": "0.2.0", + "resolved": "file:../../packages/dddlibrary", + "license": "AGPL", + "dependencies": { + "@nestjs/event-emitter": "^1.4.2", + "@nestjs/microservices": "^9.4.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@nestjs/common": "^9.4.2" + } + }, + "node_modules/@mobicoop/ddd-library/node_modules/@nestjs/event-emitter": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-1.4.2.tgz", + "integrity": "sha512-5mskPMS4KVH6LghC+NynfdmGiMCOOv9CdgVpuWGipLrJECv5KWc7vaW5o/9BYrcqPkN7Ted6CJ+O4AfsTiRlgw==", + "dependencies": { + "eventemitter2": "6.4.9" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.12" + } + }, "node_modules/@nestjs/axios": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz", @@ -1573,14 +1524,14 @@ } }, "node_modules/@nestjs/cli": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.3.0.tgz", - "integrity": "sha512-v/E8Y3zFk30+FljETvPgpoGIUiOfWuOe6WUFw3ExGfDeWrF/A8ceupDHPWNknBAqvNtz2kVrWu5mwsZUEKGIgg==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.5.0.tgz", + "integrity": "sha512-Z7q+3vNsQSG2d2r2Hl/OOj5EpfjVx3OfnJ9+KuAsOdw1sKLm7+Zc6KbhMFTd/eIvfx82ww3Nk72xdmfPYCulWA==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "@angular-devkit/schematics-cli": "15.2.4", + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", + "@angular-devkit/schematics-cli": "16.0.1", "@nestjs/schematics": "^9.0.4", "chalk": "4.1.2", "chokidar": "3.5.3", @@ -1591,14 +1542,14 @@ "node-emoji": "1.11.0", "ora": "5.4.1", "os-name": "4.0.1", - "rimraf": "4.4.0", + "rimraf": "4.4.1", "shelljs": "0.8.5", "source-map-support": "0.5.21", "tree-kill": "1.2.2", - "tsconfig-paths": "4.1.2", + "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.0.1", "typescript": "4.9.5", - "webpack": "5.76.2", + "webpack": "5.82.1", "webpack-node-externals": "3.0.0" }, "bin": { @@ -1618,9 +1569,9 @@ } }, "node_modules/@nestjs/cli/node_modules/glob": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", - "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1636,9 +1587,9 @@ } }, "node_modules/@nestjs/cli/node_modules/minimatch": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.3.tgz", - "integrity": "sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -1650,10 +1601,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@nestjs/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@nestjs/cli/node_modules/rimraf": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.0.tgz", - "integrity": "sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", "dev": true, "dependencies": { "glob": "^9.2.0" @@ -1678,9 +1638,9 @@ } }, "node_modules/@nestjs/cli/node_modules/tsconfig-paths": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", - "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { "json5": "^2.2.2", @@ -1691,14 +1651,61 @@ "node": ">=6" } }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.82.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.1.tgz", + "integrity": "sha512-C6uiGQJ+Gt4RyHXXYt+v9f+SN1v83x68URwgxNQ98cvH8kxiuywWGP4XeNZ1paOzZ63aY3cTciCEQJNFUljlLw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.14.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, "node_modules/@nestjs/common": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.2.1.tgz", - "integrity": "sha512-nZuo3oDsSSlC5mti/M2aCWTEIfHPGDXmBwWgPeCpRbrNz3IWd109rkajll+yxgidVjznAdBS9y00JkAVJblNYw==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.3.tgz", + "integrity": "sha512-Gd6D4IaYj01o14Bwv81ukidn4w3bPHCblMUq+SmUmWLyosK+XQmInCS09SbDDZyL8jy86PngtBLTdhJ2bXSUig==", "dependencies": { "iterare": "1.2.1", - "tslib": "2.4.1", - "uuid": "9.0.0" + "tslib": "2.5.3", + "uid": "2.0.2" }, "funding": { "type": "opencollective", @@ -1724,14 +1731,14 @@ } }, "node_modules/@nestjs/config": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.2.0.tgz", - "integrity": "sha512-78Eg6oMbCy3D/YvqeiGBTOWei1Jwi3f2pSIZcZ1QxY67kYsJzTRTkwRT8Iv30DbK0sGKc1mcloDLD5UXgZAZtg==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.4.tgz", + "integrity": "sha512-IGdSF+0F9MJO6dCRTEahdxPz4iVijjtolcFBxnY+2QYM3bXYQvAgzskGZi+WkAFJN/VzR3TEp60gN5sI74GxPA==", "dependencies": { - "dotenv": "16.0.1", - "dotenv-expand": "8.0.3", + "dotenv": "16.1.4", + "dotenv-expand": "10.0.0", "lodash": "4.17.21", - "uuid": "8.3.2" + "uuid": "9.0.0" }, "peerDependencies": { "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", @@ -1739,27 +1746,18 @@ "rxjs": "^6.0.0 || ^7.2.0" } }, - "node_modules/@nestjs/config/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@nestjs/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.2.1.tgz", - "integrity": "sha512-a9GkXuu8uXgNgCVW+17iI8kLCltO+HwHpU2IhR+32JKnN2WEQ1YEWU4t3GJ2MNq44YkjIw9zrKvFkjJBlYrNbQ==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.4.3.tgz", + "integrity": "sha512-Qi63+wi55Jh4sDyaj5Hhx2jOpKqT386aeo+VOKsxnd+Ql9VvkO/FjmuwBGUyzkJt29ENYc+P0Sx/k5LtstNpPQ==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "object-hash": "3.0.0", "path-to-regexp": "3.2.0", - "tslib": "2.4.1", - "uuid": "9.0.0" + "tslib": "2.5.3", + "uid": "2.0.2" }, "funding": { "type": "opencollective", @@ -1786,11 +1784,11 @@ } }, "node_modules/@nestjs/cqrs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.1.tgz", - "integrity": "sha512-DsqkAaIHy9+Ot4FJybmlDQk0HBDTGztz0F3LcMnatFDQv1X56W0Y2+R/7jjwHsmHzQfbffT2FGyCOx2/9apFCg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.4.tgz", + "integrity": "sha512-nWDF+xs4jqs6OjxFg/wVSd0NiIV9+EFCJrJNTo4VRWe78CcAaitbp56CBspUh4gKyfkci95i+EhHdEqRXKFptg==", "dependencies": { - "uuid": "8.3.2" + "uuid": "9.0.0" }, "peerDependencies": { "@nestjs/common": "^9.0.0", @@ -1799,21 +1797,26 @@ "rxjs": "^7.2.0" } }, - "node_modules/@nestjs/cqrs/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/@nestjs/event-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-2.0.0.tgz", + "integrity": "sha512-fZRv3+PmqXcbqCDRXRWhKDa+v3gmPUq4x5sQE5reVlDtEaCoAXwtGrtNswPtqd0msjyo8OWZF9k1sFjeRL6Xag==", + "dependencies": { + "eventemitter2": "6.4.9" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.12" } }, "node_modules/@nestjs/microservices": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/microservices/-/microservices-9.2.1.tgz", - "integrity": "sha512-ve4dqgoIaUG9VtinHHGCvf6SvtPjd3I44Mj8z8mSkcB8BEPfJnf4FNOShFBgsedtxLaiZyjpv8NCLcNVv5KKGg==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/microservices/-/microservices-9.4.3.tgz", + "integrity": "sha512-piMw8d3C4ppc5St5AhQEtecMhyeBK2Q1VYk4AL3NKtG6U0fzz/6KLiETpWdKXmazeI/m7qac2upOvwmRzle0aA==", "dependencies": { "iterare": "1.2.1", - "tslib": "2.4.1" + "tslib": "2.5.3" }, "funding": { "type": "opencollective", @@ -1865,15 +1868,15 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.2.1.tgz", - "integrity": "sha512-7PecaXt8lrdS1p6Vb1X/am3GGv+EO1VahyDzaEGOK6C0zwhc0VPfLtwihkjjfhS6BjpRIXXgviwEjONUvxVZnA==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.4.3.tgz", + "integrity": "sha512-FpdczWoRSC0zz2dNL9u2AQLXKXRVtq4HgHklAhbL59X0uy+mcxhlSThG7DHzDMkoSnuuHY8ojDVf7mDxk+GtCw==", "dependencies": { - "body-parser": "1.20.1", + "body-parser": "1.20.2", "cors": "2.8.5", "express": "4.18.2", "multer": "1.4.4-lts.1", - "tslib": "2.4.1" + "tslib": "2.5.3" }, "funding": { "type": "opencollective", @@ -1885,13 +1888,13 @@ } }, "node_modules/@nestjs/schematics": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.1.0.tgz", - "integrity": "sha512-/7CyMTnPJSK9/xD9CkCqwuHPOlHVlLC2RDnbdCJ7mIO07SdbBbY14msTqtYW9VRQtsjZPLh1GTChf7ryJUImwA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.2.0.tgz", + "integrity": "sha512-wHpNJDPzM6XtZUOB3gW0J6mkFCSJilzCM3XrHI1o0C8vZmFE1snbmkIXNyoi1eV0Nxh1BMymcgz5vIMJgQtTqw==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", "jsonc-parser": "3.2.0", "pluralize": "8.0.0" }, @@ -1965,12 +1968,12 @@ } }, "node_modules/@nestjs/testing": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz", - "integrity": "sha512-lemXZdRSuqoZ87l0orCrS/c7gqwxeduIFOd21g9g2RUeQ4qlWPegbQDKASzbfC28klPyrgJLW4MNq7uv2JwV8w==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.4.3.tgz", + "integrity": "sha512-LDT8Ai2eKnTzvnPaJwWOK03qTaFap5uHHsJCv6dL0uKWk6hyF9jms8DjyVaGsaujCaXDG8izl1mDEER0OmxaZA==", "dev": true, "dependencies": { - "tslib": "2.4.1" + "tslib": "2.5.3" }, "funding": { "type": "opencollective", @@ -1991,6 +1994,15 @@ } } }, + "node_modules/@nicolo-ribaudo/semver-v6": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", + "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2044,12 +2056,12 @@ } }, "node_modules/@prisma/client": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz", - "integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==", + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.2.tgz", + "integrity": "sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" + "@prisma/engines-version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81" }, "engines": { "node": ">=14.17" @@ -2064,16 +2076,16 @@ } }, "node_modules/@prisma/engines": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz", - "integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==", + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.2.tgz", + "integrity": "sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw==", "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz", - "integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==" + "version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz", + "integrity": "sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg==" }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", @@ -2172,19 +2184,19 @@ "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -2210,12 +2222,12 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/bcrypt": { @@ -2253,9 +2265,9 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "version": "8.40.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", + "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2273,43 +2285,50 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", - "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dev": true, "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.31", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -2345,9 +2364,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/long": { @@ -2356,15 +2375,15 @@ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, "node_modules/@types/node": { - "version": "16.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.9.tgz", - "integrity": "sha512-nhrqXYxiQ+5B/tPorWum37VgAiefi/wmfJ1QZKGKKecC8/3HqcTTJD0O+VABSPwtseMMF7NCPVT9uGgwn0YqsQ==" + "version": "16.18.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz", + "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -2373,9 +2392,9 @@ "dev": true }, "node_modules/@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", + "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 }, "node_modules/@types/qs": { @@ -2391,17 +2410,28 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, - "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", "dev": true, "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", "@types/mime": "*", "@types/node": "*" } @@ -2413,9 +2443,9 @@ "dev": true }, "node_modules/@types/superagent": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", - "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", + "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", "dev": true, "dependencies": { "@types/cookiejar": "*", @@ -2432,20 +2462,20 @@ } }, "node_modules/@types/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", + "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, "node_modules/@types/validator": { - "version": "13.7.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz", - "integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==" + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==" }, "node_modules/@types/yargs": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz", - "integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -2458,18 +2488,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz", - "integrity": "sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz", + "integrity": "sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.46.1", - "@typescript-eslint/type-utils": "5.46.1", - "@typescript-eslint/utils": "5.46.1", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.61.0", + "@typescript-eslint/type-utils": "5.61.0", + "@typescript-eslint/utils": "5.61.0", "debug": "^4.3.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -2491,14 +2522,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.1.tgz", - "integrity": "sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.61.0.tgz", + "integrity": "sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.46.1", - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/typescript-estree": "5.46.1", + "@typescript-eslint/scope-manager": "5.61.0", + "@typescript-eslint/types": "5.61.0", + "@typescript-eslint/typescript-estree": "5.61.0", "debug": "^4.3.4" }, "engines": { @@ -2518,13 +2549,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz", - "integrity": "sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.61.0.tgz", + "integrity": "sha512-W8VoMjoSg7f7nqAROEmTt6LoBpn81AegP7uKhhW5KzYlehs8VV0ZW0fIDVbcZRcaP3aPSW+JZFua+ysQN+m/Nw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/visitor-keys": "5.46.1" + "@typescript-eslint/types": "5.61.0", + "@typescript-eslint/visitor-keys": "5.61.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2535,13 +2566,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.1.tgz", - "integrity": "sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.61.0.tgz", + "integrity": "sha512-kk8u//r+oVK2Aj3ph/26XdH0pbAkC2RiSjUYhKD+PExemG4XSjpGFeyZ/QM8lBOa7O8aGOU+/yEbMJgQv/DnCg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.46.1", - "@typescript-eslint/utils": "5.46.1", + "@typescript-eslint/typescript-estree": "5.61.0", + "@typescript-eslint/utils": "5.61.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -2562,9 +2593,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz", - "integrity": "sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.61.0.tgz", + "integrity": "sha512-ldyueo58KjngXpzloHUog/h9REmHl59G1b3a5Sng1GfBo14BkS3ZbMEb3693gnP1k//97lh7bKsp6/V/0v1veQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2575,13 +2606,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz", - "integrity": "sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.61.0.tgz", + "integrity": "sha512-Fud90PxONnnLZ36oR5ClJBLTLfU4pIWBmnvGwTbEa2cXIqj70AEDEmOmpkFComjBZ/037ueKrOdHuYmSFVD7Rw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/visitor-keys": "5.46.1", + "@typescript-eslint/types": "5.61.0", + "@typescript-eslint/visitor-keys": "5.61.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2602,18 +2633,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz", - "integrity": "sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.61.0.tgz", + "integrity": "sha512-mV6O+6VgQmVE6+xzlA91xifndPW9ElFW8vbSF0xCT/czPXVhwDewKila1jOyRwa9AE19zKnrr7Cg5S3pJVrTWQ==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.46.1", - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/typescript-estree": "5.46.1", + "@typescript-eslint/scope-manager": "5.61.0", + "@typescript-eslint/types": "5.61.0", + "@typescript-eslint/typescript-estree": "5.61.0", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -2628,12 +2659,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz", - "integrity": "sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.61.0.tgz", + "integrity": "sha512-50XQ5VdbWrX06mQXhy93WywSFZZGsv3EOjq+lqp6WC2t+j3mb6A9xYVdrRxafvK88vg9k9u+CT4l6D8PEatjKg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/types": "5.61.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2645,148 +2676,148 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -2820,9 +2851,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2832,9 +2863,9 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "peerDependencies": { "acorn": "^8" @@ -2933,27 +2964,6 @@ "node": ">=10" } }, - "node_modules/amqplib/node_modules/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==" - }, - "node_modules/amqplib/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/amqplib/node_modules/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==" - }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -3056,9 +3066,9 @@ } }, "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3068,6 +3078,14 @@ "node": ">= 6" } }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -3106,9 +3124,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", - "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -3296,9 +3314,9 @@ } }, "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -3309,18 +3327,27 @@ "node": ">= 6" } }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -3328,7 +3355,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -3371,17 +3398,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3404,9 +3420,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -3416,13 +3432,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -3527,18 +3547,20 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001439", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", - "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", + "version": "1.0.30001512", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", + "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==", "dev": true, "funding": [ { @@ -3548,6 +3570,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -3634,18 +3660,24 @@ } }, "node_modules/ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { "node": ">=8" } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/class-transformer": { @@ -3687,9 +3719,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", "dev": true, "engines": { "node": ">=6" @@ -3726,7 +3758,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3830,6 +3861,38 @@ "typedarray": "^0.0.6" } }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", @@ -3852,9 +3915,9 @@ } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -3966,9 +4029,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4086,11 +4149,14 @@ } }, "node_modules/dotenv": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", - "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", + "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, "node_modules/dotenv-cli": { @@ -4108,10 +4174,19 @@ "dotenv": "cli.js" } }, - "node_modules/dotenv-expand": { + "node_modules/dotenv-cli/node_modules/dotenv-expand": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz", "integrity": "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", "engines": { "node": ">=12" } @@ -4122,9 +4197,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.449", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.449.tgz", + "integrity": "sha512-TxLRpRUj/107ATefeP8VIUWNOv90xJxZZbCW/eIbSZQiuiFANCx2b7u+GbVc9X4gU+xnbvypNMYVM/WArE1DNQ==", "dev": true }, "node_modules/emittery": { @@ -4162,9 +4237,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -4184,9 +4259,9 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, "node_modules/escalade": { @@ -4215,13 +4290,16 @@ } }, "node_modules/eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -4230,32 +4308,29 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -4271,9 +4346,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -4316,40 +4391,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ajv": { @@ -4369,9 +4420,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -4379,6 +4430,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -4409,14 +4463,14 @@ "dev": true }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", + "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4439,9 +4493,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -4506,6 +4560,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4604,6 +4663,29 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4622,6 +4704,20 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -4643,15 +4739,15 @@ "dev": true }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -4682,9 +4778,9 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -4873,9 +4969,9 @@ } }, "node_modules/formidable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", - "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dev": true, "dependencies": { "dezalgo": "^1.0.4", @@ -4939,10 +5035,15 @@ "node": ">=8" } }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", "dev": true }, "node_modules/fs.realpath": { @@ -5006,12 +5107,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -5077,9 +5179,9 @@ "dev": true }, "node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5112,15 +5214,15 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/has": { @@ -5142,6 +5244,17 @@ "node": ">=8" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5241,9 +5354,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -5369,9 +5482,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -5470,9 +5583,9 @@ } }, "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, "node_modules/isexe": { "version": "2.0.0", @@ -5675,7 +5788,7 @@ } } }, - "node_modules/jest-cli/node_modules/jest-config": { + "node_modules/jest-config": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", @@ -6086,18 +6199,6 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jest-watcher": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", @@ -6146,16 +6247,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6266,9 +6357,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.15", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.15.tgz", - "integrity": "sha512-sLeVLmWX17VCKKulc+aDIRHS95TxoTsKMRJi5s5gJdwlqNzMWcBCtSHHruVyXjqfi67daXM2SnLf2juSrdx5Sg==" + "version": "1.10.37", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.37.tgz", + "integrity": "sha512-Z10PCaOCiAxbUxLyR31DNeeNugSVP6iv/m7UrSKS5JHziEMApJtgku4e9Q69pzzSC9LnQiM09sqsGf2ticZnMw==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -6344,20 +6435,18 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "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, "engines": { "node": ">=6" @@ -6367,9 +6456,9 @@ } }, "node_modules/magic-string": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz", - "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.13" @@ -6424,12 +6513,12 @@ } }, "node_modules/memfs": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -6527,17 +6616,17 @@ } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "engines": { "node": ">=8" } @@ -6565,6 +6654,11 @@ "node": ">=8" } }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -6605,9 +6699,15 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6648,9 +6748,9 @@ "dev": true }, "node_modules/node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, "node_modules/node-emoji": { "version": "1.11.0", @@ -6662,9 +6762,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6687,9 +6787,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.7.tgz", - "integrity": "sha512-EJ3rzxL9pTWPjk5arA0s0dgXpnyiAbJDE6wHT62g7VsgrgQgmmZ+Ru++M1BFofncWja+Pnn3rEr3fieRySAdKQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/nopt": { @@ -6746,18 +6846,10 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6797,17 +6889,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -6971,13 +7063,13 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", - "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz", + "integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==", "dev": true, "dependencies": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6987,12 +7079,12 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", "dev": true, "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/path-to-regexp": { @@ -7028,9 +7120,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -7119,9 +7211,9 @@ } }, "node_modules/prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -7173,13 +7265,13 @@ } }, "node_modules/prisma": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz", - "integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==", + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.2.tgz", + "integrity": "sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "4.7.1" + "@prisma/engines": "4.16.2" }, "bin": { "prisma": "build/index.js", @@ -7213,9 +7305,9 @@ } }, "node_modules/protobufjs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", - "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", + "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -7236,9 +7328,9 @@ } }, "node_modules/protobufjs/node_modules/long": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", - "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -7268,9 +7360,9 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -7333,9 +7425,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -7353,24 +7445,16 @@ "dev": true }, "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dependencies": { "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7400,18 +7484,6 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7435,12 +7507,12 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7482,9 +7554,9 @@ } }, "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true, "engines": { "node": ">=10" @@ -7560,9 +7632,9 @@ } }, "node_modules/rxjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", - "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dependencies": { "tslib": "^2.1.0" } @@ -7592,9 +7664,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "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, "dependencies": { "@types/json-schema": "^7.0.8", @@ -7641,9 +7713,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -7654,6 +7726,22 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -7696,9 +7784,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -7871,17 +7959,9 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" }, "node_modules/string-length": { "version": "4.0.2", @@ -7951,17 +8031,17 @@ } }, "node_modules/superagent": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.6.tgz", - "integrity": "sha512-HqSe6DSIh3hEn6cJvCkaM1BLi466f1LHi4yubR0tpewlMpk4RUFFy35bKz8SsPBwYfIIJy5eclp+3tCYAuX0bw==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", "dev": true, "dependencies": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", + "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^2.1.1", + "formidable": "^2.1.2", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0", @@ -8051,13 +8131,13 @@ } }, "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -8077,6 +8157,11 @@ "node": ">=10" } }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -8094,13 +8179,13 @@ } }, "node_modules/terser": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.2.tgz", + "integrity": "sha512-Ah19JS86ypbJzTzvUCX7KOsEIhDaRONungA4aYBjEP3JZRf4ocuDzTg4QWZnPn9DEMiMYGJPiSOy7aykoCc70w==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -8112,16 +8197,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "engines": { "node": ">= 10.13.0" @@ -8311,9 +8396,9 @@ } }, "node_modules/ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", + "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -8433,9 +8518,9 @@ } }, "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -8520,6 +8605,17 @@ "node": ">=4.2.0" } }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -8538,9 +8634,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -8550,6 +8646,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -8557,7 +8657,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -8609,9 +8709,9 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", @@ -8623,9 +8723,9 @@ } }, "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", "engines": { "node": ">= 0.10" } @@ -8675,22 +8775,23 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", + "version": "5.88.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.1.tgz", + "integrity": "sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -8699,9 +8800,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -8844,15 +8945,6 @@ "node": ">=8.12.0" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -8904,9 +8996,10 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", @@ -8918,10 +9011,9 @@ } }, "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -8939,7 +9031,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } @@ -8965,6622 +9056,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@angular-devkit/core": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz", - "integrity": "sha512-yl+0j1bMwJLKShsyCXw77tbJG8Sd21+itisPLL2MgEpLNAO252kr9zG4TLlFRJyKVftm2l1h78KjqvM5nbOXNg==", - "dev": true, - "requires": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "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": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.4.tgz", - "integrity": "sha512-/W7/vvn59PAVLzhcvD4/N/E8RDhub8ny1A7I96LTRjC5o+yvVV16YJ4YJzolrRrIEN01KmLVQJ9A58VCaweMgw==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "jsonc-parser": "3.2.0", - "magic-string": "0.29.0", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "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": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-15.2.4.tgz", - "integrity": "sha512-QTTKEH5HOkxvQtCxb2Lna2wubehkaIzA6DKUBISijPQliLomw74tzc7lXCywmMqRTbQPVRLG3kBK97hR4x67nA==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "ansi-colors": "4.1.3", - "inquirer": "8.2.4", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "dependencies": { - "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - } - } - } - }, - "@automapper/classes": { - "version": "8.7.7", - "resolved": "https://registry.npmjs.org/@automapper/classes/-/classes-8.7.7.tgz", - "integrity": "sha512-FSbvt6QE8XnhKKQZA3kpKLuLrr9x1iW+lNYTrawVLjxQ05zsCGccLxe7moMNrg1wFAVAouQKupFgCGQ7XRjmJw==", - "requires": {} - }, - "@automapper/core": { - "version": "8.7.7", - "resolved": "https://registry.npmjs.org/@automapper/core/-/core-8.7.7.tgz", - "integrity": "sha512-YfpDJ/xqwUuC0S+BLNk81ZJfeL7CmjirUX/Gk9eQyx146DKvneBZgeZ9v5rDB51Ti14jTxVHis+5JuT7W/q0TA==" - }, - "@automapper/nestjs": { - "version": "8.7.7", - "resolved": "https://registry.npmjs.org/@automapper/nestjs/-/nestjs-8.7.7.tgz", - "integrity": "sha512-9/uYY2cmN7SJjr2QxnfyXsteHrn/RHD+Dg0VMBflzK/e8Bh/KWyOve7+kaFixlUoyHe44aXs2LVaCslqt8wnhQ==", - "requires": {} - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", - "dev": true - }, - "@babel/core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", - "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-module-transforms": "^7.20.2", - "@babel/helpers": "^7.20.5", - "@babel/parser": "^7.20.5", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.5", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", - "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", - "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", - "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.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 - }, - "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 - }, - "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.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "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/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "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 - } - } - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "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" - } - }, - "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 - } - } - }, - "@golevelup/nestjs-common": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-common/-/nestjs-common-1.4.4.tgz", - "integrity": "sha512-NTjtOhHTMuGwiR3lmBQKKaRr++mHQEsh8AxtaH+/EWOYKMK2Cv/8duaH9MQ0hI3TwnouyaA5IRxYR1ZCUyNXOQ==", - "requires": { - "nanoid": "^3.2.0" - } - }, - "@golevelup/nestjs-discovery": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-3.0.0.tgz", - "integrity": "sha512-ZvkXtobTKxXB1LJanP/l6Z/Fing88IMBr3uabQpU2IWjfsstjh02qYDSU2cfD6CSmNldX5ewW5Pd+SdK2lU8Sw==", - "requires": { - "lodash": "^4.17.15" - } - }, - "@golevelup/nestjs-modules": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-modules/-/nestjs-modules-0.6.1.tgz", - "integrity": "sha512-E0STg8In8fhIivnGDJAA70+XLPHzK5bMTkCnif9FbZ8waTYDQ3T/QQL0h73k+CUFeznn1hmuEW14sNaE+8cd7w==", - "requires": { - "lodash": "^4.17.21" - } - }, - "@golevelup/nestjs-rabbitmq": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-rabbitmq/-/nestjs-rabbitmq-3.4.0.tgz", - "integrity": "sha512-Ca6JtboZL11qTWl8mFYT3phs+JAhZDhct6/gKbcNEhaaEd3IU6+hEBnjs7LDbYSAIdlpZNODy/rRGSPCFmUYsQ==", - "requires": { - "@golevelup/nestjs-common": "^1.4.4", - "@golevelup/nestjs-discovery": "^3.0.0", - "@golevelup/nestjs-modules": "^0.6.1", - "amqp-connection-manager": "^3.0.0", - "amqplib": "^0.8.0" - } - }, - "@grpc/grpc-js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.0.tgz", - "integrity": "sha512-ySMTXQuMvvswoobvN+0LsaPf7ITO2JVfJmHxQKI4cGehNrrUms+n81BlHEX7Hl/LExji6XE3fnI9U04GSkRruA==", - "requires": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - } - }, - "@grpc/proto-loader": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.4.tgz", - "integrity": "sha512-MnWjkGwqQ3W8fx94/c1CwqLsNmHHv2t0CFn+9++6+cDphC1lolpg9M2OU0iebIjK//pBNX9e94ho+gjx6vz39w==", - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", - "yargs": "^16.2.0" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@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": { - "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" - } - }, - "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" - } - }, - "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" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-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" - } - }, - "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": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - } - } - }, - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", - "dev": true, - "requires": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" - } - }, - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" - } - }, - "@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", - "requires": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - } - }, - "@nestjs/axios": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz", - "integrity": "sha512-TpoZM/0ZJ9xiC04qkRDFod93LCZ12TQARRU3ejDvBK2E8emvzM4HThOs5ePklVxce4Q1ZsnrIWqnImvoDmJYnQ==", - "requires": { - "axios": "1.2.1" - }, - "dependencies": { - "axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - } - } - }, - "@nestjs/cli": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.3.0.tgz", - "integrity": "sha512-v/E8Y3zFk30+FljETvPgpoGIUiOfWuOe6WUFw3ExGfDeWrF/A8ceupDHPWNknBAqvNtz2kVrWu5mwsZUEKGIgg==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "@angular-devkit/schematics-cli": "15.2.4", - "@nestjs/schematics": "^9.0.4", - "chalk": "4.1.2", - "chokidar": "3.5.3", - "cli-table3": "0.6.3", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "8.0.0", - "inquirer": "8.2.5", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "os-name": "4.0.1", - "rimraf": "4.4.0", - "shelljs": "0.8.5", - "source-map-support": "0.5.21", - "tree-kill": "1.2.2", - "tsconfig-paths": "4.1.2", - "tsconfig-paths-webpack-plugin": "4.0.1", - "typescript": "4.9.5", - "webpack": "5.76.2", - "webpack-node-externals": "3.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", - "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - } - }, - "minimatch": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.3.tgz", - "integrity": "sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "rimraf": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.0.tgz", - "integrity": "sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==", - "dev": true, - "requires": { - "glob": "^9.2.0" - } - }, - "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 - }, - "tsconfig-paths": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", - "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", - "dev": true, - "requires": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - } - } - }, - "@nestjs/common": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.2.1.tgz", - "integrity": "sha512-nZuo3oDsSSlC5mti/M2aCWTEIfHPGDXmBwWgPeCpRbrNz3IWd109rkajll+yxgidVjznAdBS9y00JkAVJblNYw==", - "requires": { - "iterare": "1.2.1", - "tslib": "2.4.1", - "uuid": "9.0.0" - } - }, - "@nestjs/config": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.2.0.tgz", - "integrity": "sha512-78Eg6oMbCy3D/YvqeiGBTOWei1Jwi3f2pSIZcZ1QxY67kYsJzTRTkwRT8Iv30DbK0sGKc1mcloDLD5UXgZAZtg==", - "requires": { - "dotenv": "16.0.1", - "dotenv-expand": "8.0.3", - "lodash": "4.17.21", - "uuid": "8.3.2" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@nestjs/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.2.1.tgz", - "integrity": "sha512-a9GkXuu8uXgNgCVW+17iI8kLCltO+HwHpU2IhR+32JKnN2WEQ1YEWU4t3GJ2MNq44YkjIw9zrKvFkjJBlYrNbQ==", - "requires": { - "@nuxtjs/opencollective": "0.3.2", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "object-hash": "3.0.0", - "path-to-regexp": "3.2.0", - "tslib": "2.4.1", - "uuid": "9.0.0" - } - }, - "@nestjs/cqrs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.1.tgz", - "integrity": "sha512-DsqkAaIHy9+Ot4FJybmlDQk0HBDTGztz0F3LcMnatFDQv1X56W0Y2+R/7jjwHsmHzQfbffT2FGyCOx2/9apFCg==", - "requires": { - "uuid": "8.3.2" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@nestjs/microservices": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/microservices/-/microservices-9.2.1.tgz", - "integrity": "sha512-ve4dqgoIaUG9VtinHHGCvf6SvtPjd3I44Mj8z8mSkcB8BEPfJnf4FNOShFBgsedtxLaiZyjpv8NCLcNVv5KKGg==", - "requires": { - "iterare": "1.2.1", - "tslib": "2.4.1" - } - }, - "@nestjs/platform-express": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.2.1.tgz", - "integrity": "sha512-7PecaXt8lrdS1p6Vb1X/am3GGv+EO1VahyDzaEGOK6C0zwhc0VPfLtwihkjjfhS6BjpRIXXgviwEjONUvxVZnA==", - "requires": { - "body-parser": "1.20.1", - "cors": "2.8.5", - "express": "4.18.2", - "multer": "1.4.4-lts.1", - "tslib": "2.4.1" - } - }, - "@nestjs/schematics": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.1.0.tgz", - "integrity": "sha512-/7CyMTnPJSK9/xD9CkCqwuHPOlHVlLC2RDnbdCJ7mIO07SdbBbY14msTqtYW9VRQtsjZPLh1GTChf7ryJUImwA==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "jsonc-parser": "3.2.0", - "pluralize": "8.0.0" - } - }, - "@nestjs/terminus": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-9.2.2.tgz", - "integrity": "sha512-AWUA8XLcgxWUjUFYHDqi42M7CZn2e+DEWxP+MqNAbMzz4ybB5jGcFK5Fy8qwaNBoWg6KMF1JiXOOygGXgk9ydg==", - "requires": { - "boxen": "5.1.2", - "check-disk-space": "3.3.1" - } - }, - "@nestjs/testing": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz", - "integrity": "sha512-lemXZdRSuqoZ87l0orCrS/c7gqwxeduIFOd21g9g2RUeQ4qlWPegbQDKASzbfC28klPyrgJLW4MNq7uv2JwV8w==", - "dev": true, - "requires": { - "tslib": "2.4.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@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" - } - }, - "@prisma/client": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz", - "integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==", - "requires": { - "@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" - } - }, - "@prisma/engines": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz", - "integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==", - "devOptional": true - }, - "@prisma/engines-version": { - "version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz", - "integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==" - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "@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": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "@types/express": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", - "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.31", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "28.1.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.8.tgz", - "integrity": "sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==", - "dev": true, - "requires": { - "expect": "^28.0.0", - "pretty-format": "^28.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", - "dev": true - }, - "@types/node": { - "version": "16.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.9.tgz", - "integrity": "sha512-nhrqXYxiQ+5B/tPorWum37VgAiefi/wmfJ1QZKGKKecC8/3HqcTTJD0O+VABSPwtseMMF7NCPVT9uGgwn0YqsQ==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", - "dev": true, - "requires": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/superagent": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", - "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", - "dev": true, - "requires": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "@types/supertest": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", - "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", - "dev": true, - "requires": { - "@types/superagent": "*" - } - }, - "@types/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", - "dev": true - }, - "@types/validator": { - "version": "13.7.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz", - "integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==" - }, - "@types/yargs": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz", - "integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz", - "integrity": "sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.46.1", - "@typescript-eslint/type-utils": "5.46.1", - "@typescript-eslint/utils": "5.46.1", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.1.tgz", - "integrity": "sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.46.1", - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/typescript-estree": "5.46.1", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz", - "integrity": "sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/visitor-keys": "5.46.1" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.1.tgz", - "integrity": "sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.46.1", - "@typescript-eslint/utils": "5.46.1", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz", - "integrity": "sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz", - "integrity": "sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/visitor-keys": "5.46.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz", - "integrity": "sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.46.1", - "@typescript-eslint/types": "5.46.1", - "@typescript-eslint/typescript-estree": "5.46.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz", - "integrity": "sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.46.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "accepts": { - "version": "1.3.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.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - } - }, - "amqp-connection-manager": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/amqp-connection-manager/-/amqp-connection-manager-3.9.0.tgz", - "integrity": "sha512-ZKw9ckJKz40Lc2pC7DY0NVocpzPalMaCgv0sBn+N4er2QFAJul9pIiMOm/FsPHeCzB+FulV7PckOpmZvWvewGQ==", - "requires": { - "promise-breaker": "^5.0.0" - } - }, - "amqplib": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.8.0.tgz", - "integrity": "sha512-icU+a4kkq4Y1PS4NNi+YPDMwdlbFcZ1EZTQT2nigW3fvOb6AOgUQ9+Mk4ue0Zu5cBg/XpDzB40oH10ysrk2dmA==", - "requires": { - "bitsyntax": "~0.1.0", - "bluebird": "^3.7.2", - "buffer-more-ints": "~1.0.0", - "readable-stream": "1.x >=1.1.9", - "safe-buffer": "~5.2.1", - "url-parse": "~1.5.1" - }, - "dependencies": { - "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==" - }, - "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" - } - }, - "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==" - } - } - }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "requires": { - "string-width": "^4.1.0" - } - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "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" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "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==" - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "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-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", - "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dev": true, - "requires": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^28.1.3", - "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==" - }, - "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 - }, - "bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "requires": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.0" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bitsyntax": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz", - "integrity": "sha512-ikAdCnrloKmFOugAfxWws89/fPc+nw0OOG1IzIE72uSOg/A3cYptKCjSUhDTuj7fhsJtzkzlv7l3b8PzRHLN0Q==", - "requires": { - "buffer-more-ints": "~1.0.0", - "debug": "~2.6.9", - "safe-buffer": "~5.1.2" - }, - "dependencies": { - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==" - } - } - }, - "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": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - } - } - }, - "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" - } - }, - "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "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==" - }, - "buffer-more-ints": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", - "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" - }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { - "streamsearch": "^1.1.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "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.30001439", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", - "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", - "dev": true - }, - "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 - }, - "check-disk-space": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.3.1.tgz", - "integrity": "sha512-iOrT8yCZjSnyNZ43476FE2rnssvgw5hnuwOM0hm8Nj1qa0v4ieUUEbCyxxsEliaoDUb/75yCOL71zkDiDBLbMQ==" - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "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": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" - }, - "class-validator": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", - "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==", - "requires": { - "@types/validator": "^13.7.10", - "libphonenumber-js": "^1.10.14", - "validator": "^13.7.0" - } - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" - }, - "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.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true - }, - "cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "string-width": "^4.2.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": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "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.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "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==" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "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" - } - }, - "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "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.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "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 - }, - "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": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "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.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "defaults": { - "version": "1.0.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" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dotenv": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", - "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" - }, - "dotenv-cli": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-6.0.0.tgz", - "integrity": "sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "dotenv": "^16.0.0", - "dotenv-expand": "^8.0.1", - "minimist": "^1.2.5" - } - }, - "dotenv-expand": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz", - "integrity": "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==" - }, - "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.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "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==" - }, - "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.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "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-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "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": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "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" - } - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "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 - } - } - }, - "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "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 - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==" - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "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" - }, - "dependencies": { - "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 - } - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" - }, - "fork-ts-checker-webpack-plugin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", - "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", - "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", - "dev": true, - "requires": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "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.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, - "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==" - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "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": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "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==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "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": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", - "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.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - } - }, - "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-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-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-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-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-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-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-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-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 - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "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": { - "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.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterare": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" - }, - "jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", - "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/types": "^28.1.3", - "import-local": "^3.0.2", - "jest-cli": "^28.1.3" - } - }, - "jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "dependencies": { - "jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - } - } - }, - "jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" - } - }, - "jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true - }, - "jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", - "dev": true, - "requires": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" - } - }, - "jest-runner": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "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-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" - } - }, - "jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "leven": "^3.1.0", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "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-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "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" - } - }, - "libphonenumber-js": { - "version": "1.10.15", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.15.tgz", - "integrity": "sha512-sLeVLmWX17VCKKulc+aDIRHS95TxoTsKMRJi5s5gJdwlqNzMWcBCtSHHruVyXjqfi67daXM2SnLf2juSrdx5Sg==" - }, - "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 - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "lodash.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.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "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" - } - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", - "dev": true - }, - "magic-string": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz", - "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "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" - } - }, - "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.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", - "dev": true, - "requires": { - "fs-monkey": "^1.0.3" - } - }, - "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 - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "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.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" - }, - "minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==" - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "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.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multer": { - "version": "1.4.4-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", - "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "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 - }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "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 - }, - "node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true - }, - "node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" - }, - "node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "requires": { - "lodash": "^4.17.21" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "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-releases": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.7.tgz", - "integrity": "sha512-EJ3rzxL9pTWPjk5arA0s0dgXpnyiAbJDE6wHT62g7VsgrgQgmmZ+Ru++M1BFofncWja+Pnn3rEr3fieRySAdKQ==", - "dev": true - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-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" - } - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "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" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "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.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", - "dev": true, - "requires": { - "macos-release": "^2.5.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-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "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" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "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-scurry": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", - "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", - "dev": true, - "requires": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "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.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-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" - } - } - } - }, - "pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prisma": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz", - "integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==", - "devOptional": true, - "requires": { - "@prisma/engines": "4.7.1" - } - }, - "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==" - }, - "promise-breaker": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/promise-breaker/-/promise-breaker-5.0.0.tgz", - "integrity": "sha512-mgsWQuG4kJ1dtO6e/QlNDLFtMkMzzecsC69aI5hlLEjGHFNpHrvGhFi4LiK5jg2SMQj74/diH+wZliL9LpGsyA==" - }, - "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" - } - }, - "protobufjs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", - "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "dependencies": { - "long": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", - "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" - } - } - }, - "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" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "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.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "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.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "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==" - } - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "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.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "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" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "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" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", - "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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, - "requires": {} - }, - "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 - } - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "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" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "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==" - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true - }, - "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 - } - } - }, - "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 - } - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" - }, - "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" - }, - "dependencies": { - "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==" - } - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "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": "8.0.6", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.6.tgz", - "integrity": "sha512-HqSe6DSIh3hEn6cJvCkaM1BLi466f1LHi4yubR0tpewlMpk4RUFFy35bKz8SsPBwYfIIJy5eclp+3tCYAuX0bw==", - "dev": true, - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.1", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - } - } - }, - "supertest": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", - "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^8.0.5" - } - }, - "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": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } - } - }, - "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.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "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.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.14", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" - }, - "dependencies": { - "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" - } - }, - "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" - } - } - } - }, - "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 - }, - "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-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.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "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": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - } - }, - "ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4" - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "tsconfig-paths": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz", - "integrity": "sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==", - "dev": true, - "requires": { - "json5": "^2.2.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "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 - } - } - }, - "tsconfig-paths-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^4.1.2" - }, - "dependencies": { - "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 - }, - "tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "requires": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - } - } - }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "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.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "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==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "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": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "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.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "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.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - } - }, - "webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "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" - } - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, - "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "requires": { - "execa": "^4.0.2" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "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 - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "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-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "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": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index c309b30..c28416c 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,13 @@ "@golevelup/nestjs-rabbitmq": "^3.4.0", "@grpc/grpc-js": "^1.8.0", "@grpc/proto-loader": "^0.7.4", + "@mobicoop/ddd-library": "file:../../packages/dddlibrary", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.2.0", "@nestjs/core": "^9.0.0", "@nestjs/cqrs": "^9.0.1", + "@nestjs/event-emitter": "^2.0.0", "@nestjs/microservices": "^9.2.1", "@nestjs/platform-express": "^9.0.0", "@nestjs/terminus": "^9.2.2", diff --git a/prisma/migrations/20221220135616_init/migration.sql b/prisma/migrations/20230704081950_init/migration.sql similarity index 68% rename from prisma/migrations/20221220135616_init/migration.sql rename to prisma/migrations/20230704081950_init/migration.sql index eedb8ce..016fdfc 100644 --- a/prisma/migrations/20221220135616_init/migration.sql +++ b/prisma/migrations/20230704081950_init/migration.sql @@ -14,7 +14,7 @@ CREATE TABLE "auth" ( -- CreateTable CREATE TABLE "username" ( "username" TEXT NOT NULL, - "uuid" UUID NOT NULL, + "authUuid" UUID NOT NULL, "type" "Type" NOT NULL DEFAULT 'EMAIL', "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, @@ -23,4 +23,7 @@ CREATE TABLE "username" ( ); -- CreateIndex -CREATE UNIQUE INDEX "username_uuid_type_key" ON "username"("uuid", "type"); +CREATE UNIQUE INDEX "username_authUuid_type_key" ON "username"("authUuid", "type"); + +-- AddForeignKey +ALTER TABLE "username" ADD CONSTRAINT "username_authUuid_fkey" FOREIGN KEY ("authUuid") REFERENCES "auth"("uuid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 49c2ce7..e78e2f8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,22 +11,24 @@ datasource db { } model Auth { - uuid String @id @db.Uuid + uuid String @id @default(uuid()) @db.Uuid password String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + usernames Username[] @@map("auth") } model Username { username String @id - uuid String @db.Uuid + authUuid String @db.Uuid type Type @default(EMAIL) // type is needed in case of username update createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + Auth Auth @relation(fields: [authUuid], references: [uuid], onDelete: Cascade) - @@unique([uuid, type]) + @@unique([authUuid, type]) @@map("username") } diff --git a/src/app.module.ts b/src/app.module.ts index e7a7114..14e1abd 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,13 +2,15 @@ import { classes } from '@automapper/classes'; import { AutomapperModule } from '@automapper/nestjs'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { AuthenticationModule } from './modules/authentication/authentication.module'; import { AuthorizationModule } from './modules/authorization/authorization.module'; import { HealthModule } from './modules/health/health.module'; +import { AuthenticationModule } from '@modules/newauthentication/authentication.module'; +import { EventEmitterModule } from '@nestjs/event-emitter'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), + EventEmitterModule.forRoot(), AutomapperModule.forRoot({ strategyInitializer: classes() }), AuthenticationModule, AuthorizationModule, diff --git a/src/main.ts b/src/main.ts index ac5b6ca..1d38e94 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,7 +15,7 @@ async function bootstrap() { protoPath: [ join( __dirname, - 'modules/authentication/adapters/primaries/authentication.proto', + 'modules/newauthentication/interface/grpc-controllers/authentication.proto', ), join( __dirname, diff --git a/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts b/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts index c62eaf2..02c5cf9 100644 --- a/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts +++ b/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts @@ -79,7 +79,7 @@ export abstract class PrismaRepository implements IRepository { // TODO : using any is not good, but needed for nested entities // TODO : Refactor for good clean architecture ? - create = async (entity: Partial | any, include?: any): Promise => { + async create(entity: Partial | any, include?: any): Promise { try { const res = await this.prisma[this.model].create({ data: entity, @@ -98,7 +98,7 @@ export abstract class PrismaRepository implements IRepository { throw new DatabaseException(); } } - }; + } update = async (uuid: string, entity: Partial): Promise => { try { diff --git a/src/modules/newauthentication/authentication.di-tokens.ts b/src/modules/newauthentication/authentication.di-tokens.ts new file mode 100644 index 0000000..c1afd82 --- /dev/null +++ b/src/modules/newauthentication/authentication.di-tokens.ts @@ -0,0 +1 @@ +export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY'); diff --git a/src/modules/newauthentication/authentication.mapper.ts b/src/modules/newauthentication/authentication.mapper.ts new file mode 100644 index 0000000..d0391ab --- /dev/null +++ b/src/modules/newauthentication/authentication.mapper.ts @@ -0,0 +1,66 @@ +import { Mapper } from '@mobicoop/ddd-library'; +import { Injectable } from '@nestjs/common'; +import { AuthenticationEntity } from './core/domain/authentication.entity'; +import { AuthenticationResponseDto } from './interface/dtos/authentication.response.dto'; +import { + AuthenticationReadModel, + AuthenticationWriteModel, + UsernameModel, +} from './infrastructure/authentication.repository'; +import { Type, UsernameProps } from './core/domain/username.types'; + +/** + * Mapper constructs objects that are used in different layers: + * Record is an object that is stored in a database, + * Entity is an object that is used in application domain layer, + * and a ResponseDTO is an object returned to a user (usually as json). + */ + +@Injectable() +export class AuthenticationMapper + implements + Mapper< + AuthenticationEntity, + AuthenticationReadModel, + AuthenticationWriteModel, + AuthenticationResponseDto + > +{ + toPersistence = (entity: AuthenticationEntity): AuthenticationWriteModel => { + const copy = entity.getProps(); + const record: AuthenticationWriteModel = { + uuid: copy.id, + password: copy.password, + usernames: { + create: copy.usernames.map((username: UsernameProps) => ({ + username: username.name, + type: username.type, + })), + }, + createdAt: copy.createdAt, + updatedAt: copy.updatedAt, + }; + return record; + }; + + toDomain = (record: AuthenticationReadModel): AuthenticationEntity => { + const entity = new AuthenticationEntity({ + id: record.uuid, + createdAt: new Date(record.createdAt), + updatedAt: new Date(record.updatedAt), + props: { + password: record.password, + usernames: record.usernames.map((username: UsernameModel) => ({ + name: username.username, + type: Type[username.type], + })), + }, + }); + return entity; + }; + + toResponse = (entity: AuthenticationEntity): AuthenticationResponseDto => { + const response = new AuthenticationResponseDto(entity); + return response; + }; +} diff --git a/src/modules/newauthentication/authentication.module.ts b/src/modules/newauthentication/authentication.module.ts new file mode 100644 index 0000000..83f5270 --- /dev/null +++ b/src/modules/newauthentication/authentication.module.ts @@ -0,0 +1,31 @@ +import { Module, Provider } from '@nestjs/common'; +import { CreateAuthenticationGrpcController } from './interface/grpc-controllers/create-authentication.grpc.controller'; +import { CreateAuthenticationService } from './core/application/commands/create-authentication/create-authentication.service'; +import { AuthenticationMapper } from './authentication.mapper'; +import { AUTHENTICATION_REPOSITORY } from './authentication.di-tokens'; +import { AuthenticationRepository } from './infrastructure/authentication.repository'; +import { PrismaService } from './infrastructure/prisma.service'; +import { CqrsModule } from '@nestjs/cqrs'; + +const grpcControllers = [CreateAuthenticationGrpcController]; + +const commandHandlers: Provider[] = [CreateAuthenticationService]; + +const mappers: Provider[] = [AuthenticationMapper]; + +const repositories: Provider[] = [ + { + provide: AUTHENTICATION_REPOSITORY, + useClass: AuthenticationRepository, + }, +]; + +const orms: Provider[] = [PrismaService]; + +@Module({ + imports: [CqrsModule], + controllers: [...grpcControllers], + providers: [...commandHandlers, ...mappers, ...repositories, ...orms], + exports: [PrismaService, AuthenticationMapper, AUTHENTICATION_REPOSITORY], +}) +export class AuthenticationModule {} diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts new file mode 100644 index 0000000..c5c3c79 --- /dev/null +++ b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts @@ -0,0 +1,13 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; +import { Username } from '../types/username'; + +export class CreateAuthenticationCommand extends Command { + readonly password: string; + readonly usernames: Username[]; + + constructor(props: CommandProps) { + super(props); + this.password = props.password; + this.usernames = props.usernames; + } +} diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts new file mode 100644 index 0000000..d78c895 --- /dev/null +++ b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts @@ -0,0 +1,47 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { Inject } from '@nestjs/common'; +import { + AggregateID, + ConflictException, + UniqueConflictException, +} from '@mobicoop/ddd-library'; +import { CreateAuthenticationCommand } from './create-authentication.command'; +import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { + AuthenticationAlreadyExistsException, + UsernameAlreadyExistsException, +} from '@modules/newauthentication/core/domain/authentication.errors'; + +@CommandHandler(CreateAuthenticationCommand) +export class CreateAuthenticationService implements ICommandHandler { + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly repository: AuthenticationRepositoryPort, + ) {} + + async execute(command: CreateAuthenticationCommand): Promise { + const authentication = await AuthenticationEntity.create({ + password: command.password, + usernames: command.usernames, + }); + + try { + await this.repository.insert(authentication); + return authentication.id; + } catch (error: any) { + console.log('error', error.cause); + if (error instanceof ConflictException) { + throw new AuthenticationAlreadyExistsException(error); + } + if ( + error instanceof UniqueConflictException && + error.message.includes('username') + ) { + throw new UsernameAlreadyExistsException(error); + } + throw error; + } + } +} diff --git a/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts b/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts new file mode 100644 index 0000000..3b8ad0a --- /dev/null +++ b/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts @@ -0,0 +1,4 @@ +import { RepositoryPort } from '@mobicoop/ddd-library'; +import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; + +export type AuthenticationRepositoryPort = RepositoryPort; diff --git a/src/modules/newauthentication/core/application/commands/types/username.ts b/src/modules/newauthentication/core/application/commands/types/username.ts new file mode 100644 index 0000000..b8c4452 --- /dev/null +++ b/src/modules/newauthentication/core/application/commands/types/username.ts @@ -0,0 +1,6 @@ +import { Type } from '@modules/newauthentication/core/domain/username.types'; + +export type Username = { + name: string; + type: Type; +}; diff --git a/src/modules/newauthentication/core/domain/authentication.entity.ts b/src/modules/newauthentication/core/domain/authentication.entity.ts new file mode 100644 index 0000000..3f44168 --- /dev/null +++ b/src/modules/newauthentication/core/domain/authentication.entity.ts @@ -0,0 +1,35 @@ +import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; +import { v4 } from 'uuid'; +import * as bcrypt from 'bcrypt'; +import { + AuthenticationProps, + CreateAuthenticationProps, +} from './authentication.types'; +import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-events'; + +export class AuthenticationEntity extends AggregateRoot { + protected readonly _id: AggregateID; + + static create = async ( + create: CreateAuthenticationProps, + ): Promise => { + const id = v4(); + const props: AuthenticationProps = { ...create }; + const hash = await bcrypt.hash(props.password, 10); + const authentication = new AuthenticationEntity({ + id, + props: { + password: hash, + usernames: props.usernames, + }, + }); + authentication.addEvent( + new AuthenticationCreatedDomainEvent({ aggregateId: id }), + ); + return authentication; + }; + + validate(): void { + // entity business rules validation to protect it's invariant before saving entity to a database + } +} diff --git a/src/modules/newauthentication/core/domain/authentication.errors.ts b/src/modules/newauthentication/core/domain/authentication.errors.ts new file mode 100644 index 0000000..37de692 --- /dev/null +++ b/src/modules/newauthentication/core/domain/authentication.errors.ts @@ -0,0 +1,21 @@ +import { ExceptionBase } from '@mobicoop/ddd-library'; + +export class AuthenticationAlreadyExistsException extends ExceptionBase { + static readonly message = 'Authentication already exists'; + + public readonly code = 'AUTHENTICATION.ALREADY_EXISTS'; + + constructor(cause?: Error, metadata?: unknown) { + super(AuthenticationAlreadyExistsException.message, cause, metadata); + } +} + +export class UsernameAlreadyExistsException extends ExceptionBase { + static readonly message = 'Username already exists'; + + public readonly code = 'USERNAME.ALREADY_EXISTS'; + + constructor(cause?: Error, metadata?: unknown) { + super(UsernameAlreadyExistsException.message, cause, metadata); + } +} diff --git a/src/modules/newauthentication/core/domain/authentication.types.ts b/src/modules/newauthentication/core/domain/authentication.types.ts new file mode 100644 index 0000000..afbfd55 --- /dev/null +++ b/src/modules/newauthentication/core/domain/authentication.types.ts @@ -0,0 +1,13 @@ +import { UsernameProps } from './username.types'; + +// All properties that an Authentication has +export interface AuthenticationProps { + password: string; + usernames: UsernameProps[]; +} + +// Properties that are needed for an Authentication creation +export interface CreateAuthenticationProps { + password: string; + usernames: UsernameProps[]; +} diff --git a/src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts b/src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts new file mode 100644 index 0000000..a70baa6 --- /dev/null +++ b/src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class AuthenticationCreatedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/newauthentication/core/domain/username.entity.ts b/src/modules/newauthentication/core/domain/username.entity.ts new file mode 100644 index 0000000..f5c4454 --- /dev/null +++ b/src/modules/newauthentication/core/domain/username.entity.ts @@ -0,0 +1,9 @@ +import { Entity } from '@mobicoop/ddd-library'; +import { UsernameProps } from './username.types'; + +export class UsernameEntity extends Entity { + protected _id: string; + validate(): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/modules/newauthentication/core/domain/username.types.ts b/src/modules/newauthentication/core/domain/username.types.ts new file mode 100644 index 0000000..a774c26 --- /dev/null +++ b/src/modules/newauthentication/core/domain/username.types.ts @@ -0,0 +1,16 @@ +// All properties that a Username has +export interface UsernameProps { + name: string; + type: Type; +} + +// Properties that are needed for a Username creation +export interface CreateUsernameProps { + name: string; + type: Type; +} + +export enum Type { + EMAIL = 'EMAIL', + PHONE = 'PHONE', +} diff --git a/src/modules/newauthentication/infrastructure/authentication.repository.ts b/src/modules/newauthentication/infrastructure/authentication.repository.ts new file mode 100644 index 0000000..f003d3d --- /dev/null +++ b/src/modules/newauthentication/infrastructure/authentication.repository.ts @@ -0,0 +1,56 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { PrismaRepositoryBase } from '@mobicoop/ddd-library'; +import { AuthenticationEntity } from '../core/domain/authentication.entity'; +import { AuthenticationRepositoryPort } from '../core/application/commands/ports/authentication.repository.port'; +import { PrismaService } from './prisma.service'; +import { AuthenticationMapper } from '../authentication.mapper'; + +export type AuthenticationBaseModel = { + uuid: string; + password: string; + createdAt: Date; + updatedAt: Date; +}; + +export type AuthenticationReadModel = AuthenticationBaseModel & { + usernames: UsernameModel[]; +}; + +export type AuthenticationWriteModel = AuthenticationBaseModel & { + usernames: { + create: UsernameModel[]; + }; +}; + +export type UsernameModel = { + username: string; + type: string; +}; + +/** + * Repository is used for retrieving/saving domain entities + * */ +@Injectable() +export class AuthenticationRepository + extends PrismaRepositoryBase< + AuthenticationEntity, + AuthenticationReadModel, + AuthenticationWriteModel + > + implements AuthenticationRepositoryPort +{ + constructor( + prisma: PrismaService, + mapper: AuthenticationMapper, + eventEmitter: EventEmitter2, + ) { + super( + prisma.auth, + prisma, + mapper, + eventEmitter, + new Logger(AuthenticationRepository.name), + ); + } +} diff --git a/src/modules/newauthentication/infrastructure/prisma.service.ts b/src/modules/newauthentication/infrastructure/prisma.service.ts new file mode 100644 index 0000000..edf6532 --- /dev/null +++ b/src/modules/newauthentication/infrastructure/prisma.service.ts @@ -0,0 +1,15 @@ +import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit { + async onModuleInit() { + await this.$connect(); + } + + async enableShutdownHooks(app: INestApplication) { + this.$on('beforeExit', async () => { + await app.close(); + }); + } +} diff --git a/src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts b/src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts new file mode 100644 index 0000000..e456802 --- /dev/null +++ b/src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts @@ -0,0 +1,6 @@ +import { PaginatedResponseDto } from '@mobicoop/ddd-library'; +import { AuthenticationResponseDto } from './authentication.response.dto'; + +export class AauthenticationPaginatedResponseDto extends PaginatedResponseDto { + readonly data: readonly AuthenticationResponseDto[]; +} diff --git a/src/modules/newauthentication/interface/dtos/authentication.response.dto.ts b/src/modules/newauthentication/interface/dtos/authentication.response.dto.ts new file mode 100644 index 0000000..812e97f --- /dev/null +++ b/src/modules/newauthentication/interface/dtos/authentication.response.dto.ts @@ -0,0 +1,3 @@ +import { ResponseBase } from '@mobicoop/ddd-library'; + +export class AuthenticationResponseDto extends ResponseBase {} diff --git a/src/modules/newauthentication/interface/grpc-controllers/authentication.proto b/src/modules/newauthentication/interface/grpc-controllers/authentication.proto new file mode 100644 index 0000000..36a5af6 --- /dev/null +++ b/src/modules/newauthentication/interface/grpc-controllers/authentication.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package authentication; + +service AuthenticationService { + rpc Validate(AuthenticationByUsernamePassword) returns (Id); + rpc Create(Authentication) returns (Id); + rpc AddUsername(Username) returns (Id); + rpc UpdatePassword(Password) returns (Id); + rpc UpdateUsername(Username) returns (Id); + rpc DeleteUsername(Username) returns (Id); + rpc Delete(Id) returns (Empty); +} + +message AuthenticationByUsernamePassword { + string username = 1; + string password = 2; +} + +message Authentication { + repeated Username usernames = 1; + string password = 2; +} + +message Password { + string id = 1; + string password = 2; +} + +message Username { + string id = 1; + string name = 2; + string type = 3; +} + +message Id { + string id = 1; +} + +message Empty {} diff --git a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts b/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts new file mode 100644 index 0000000..55bca88 --- /dev/null +++ b/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts @@ -0,0 +1,43 @@ +import { + AggregateID, + IdResponse, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { CreateAuthenticationRequestDto } from './dtos/create-authentication.request.dto'; +import { CreateAuthenticationCommand } from '@modules/newauthentication/core/application/commands/create-authentication/create-authentication.command'; +import { AuthenticationAlreadyExistsException } from '@modules/newauthentication/core/domain/authentication.errors'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class CreateAuthenticationGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'Create') + async validate(data: CreateAuthenticationRequestDto): Promise { + try { + const aggregateID: AggregateID = await this.commandBus.execute( + new CreateAuthenticationCommand(data), + ); + return new IdResponse(aggregateID); + } catch (error: any) { + if (error instanceof AuthenticationAlreadyExistsException) + throw new RpcException({ + code: RpcExceptionCode.ALREADY_EXISTS, + message: error.message, + }); + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts b/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts new file mode 100644 index 0000000..91b5765 --- /dev/null +++ b/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts @@ -0,0 +1,21 @@ +import { Type } from 'class-transformer'; +import { + ArrayMinSize, + IsArray, + IsNotEmpty, + IsString, + ValidateNested, +} from 'class-validator'; +import { UsernameDto } from './username.dto'; + +export class CreateAuthenticationRequestDto { + @Type(() => UsernameDto) + @IsArray() + @ArrayMinSize(1) + @ValidateNested({ each: true }) + usernames: UsernameDto[]; + + @IsString() + @IsNotEmpty() + password: string; +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts b/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts new file mode 100644 index 0000000..665ee21 --- /dev/null +++ b/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts @@ -0,0 +1,11 @@ +import { Type } from '@modules/newauthentication/core/domain/username.types'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; + +export class UsernameDto { + @IsString() + name: string; + + @IsEnum(Type) + @IsNotEmpty() + type: Type; +} diff --git a/tsconfig.json b/tsconfig.json index adb614c..0e31118 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,10 @@ "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + "paths": { + "@modules/*": ["src/modules/*"], + "@src/*": ["src/*"] + } } } From bbcd2cdb9ea975c21b138a3e7fddadbc39db705f Mon Sep 17 00:00:00 2001 From: sbriat Date: Wed, 5 Jul 2023 17:32:21 +0200 Subject: [PATCH 02/23] delete authentication, use custom logger --- .env.dist | 8 +-- .env.test | 2 +- package-lock.json | 54 +++++++++++++++++++ package.json | 1 + prisma/schema.prisma | 3 +- src/app.di-tokens.ts | 1 + src/app.module.ts | 31 ++++++++--- .../authentication.mapper.ts | 1 + .../authentication.module.ts | 29 ++++++++-- .../create-authentication.command.ts | 2 + .../create-authentication.service.ts | 24 +++++---- .../delete-authentication.command.ts | 10 ++++ .../delete-authentication.service.ts | 26 +++++++++ .../core/domain/authentication.entity.ts | 16 ++++-- .../core/domain/authentication.types.ts | 2 + .../authentication-deleted.domain-event.ts | 7 +++ .../core/domain/username.entity.ts | 9 ---- .../core/domain/username.types.ts | 6 --- .../authentication.repository.ts | 18 +++++-- .../grpc-controllers/authentication.proto | 15 ++++-- .../create-authentication.grpc.controller.ts | 2 +- .../delete-authentication.grpc.controller.ts | 45 ++++++++++++++++ .../dtos/create-authentication.request.dto.ts | 4 ++ .../dtos/delete-authentication.request.dto.ts | 7 +++ 24 files changed, 269 insertions(+), 54 deletions(-) create mode 100644 src/app.di-tokens.ts create mode 100644 src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts create mode 100644 src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts create mode 100644 src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts delete mode 100644 src/modules/newauthentication/core/domain/username.entity.ts create mode 100644 src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts create mode 100644 src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts diff --git a/.env.dist b/.env.dist index e706084..2f11a98 100644 --- a/.env.dist +++ b/.env.dist @@ -6,10 +6,10 @@ HEALTH_SERVICE_PORT=6002 # PRISMA DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=auth" -# RABBIT MQ -RMQ_URI=amqp://v3-broker:5672 -RMQ_EXCHANGE=mobicoop +# MESSAGE BROKER +MESSAGE_BROKER_URI=amqp://v3-broker:5672 +MESSAGE_BROKER_EXCHANGE=mobicoop # OPA -OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless +OPA_IMAGE=openpolicyagent/opa:0.54.0 OPA_URL=http://v3-auth-opa:8181/v1/data/ diff --git a/.env.test b/.env.test index 57654f0..bfec28f 100644 --- a/.env.test +++ b/.env.test @@ -6,5 +6,5 @@ SERVICE_PORT=5002 DATABASE_URL="postgresql://mobicoop:mobicoop@localhost:5432/mobicoop-test?schema=auth" # OPA -OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless +OPA_IMAGE=openpolicyagent/opa:0.54.0 OPA_URL=http://v3-auth-opa:8181/v1/data/ diff --git a/package-lock.json b/package-lock.json index 7802456..77683c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@grpc/grpc-js": "^1.8.0", "@grpc/proto-loader": "^0.7.4", "@mobicoop/ddd-library": "file:../../packages/dddlibrary", + "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.2.0", @@ -72,6 +73,24 @@ "node": ">=0.10.0" } }, + "node_modules/@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -1500,6 +1519,33 @@ "reflect-metadata": "^0.1.12" } }, + "node_modules/@mobicoop/message-broker-module": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mobicoop/message-broker-module/-/message-broker-module-1.2.0.tgz", + "integrity": "sha512-RoSHHK1GyQ/QVDmm3JS/wBfh171oChvyEp6YWmJd12krFLrPVn9MoEvZdyT3I5J31oBiUabMPle5Kdpw+Nrmww==", + "dependencies": { + "@golevelup/nestjs-rabbitmq": "^3.6.0", + "@types/amqplib": "^0.10.1", + "amqplib": "^0.10.3" + }, + "peerDependencies": { + "@nestjs/common": "^9.4.2" + } + }, + "node_modules/@mobicoop/message-broker-module/node_modules/amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nestjs/axios": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz", @@ -2189,6 +2235,14 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@types/amqplib": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.1.tgz", + "integrity": "sha512-j6ANKT79ncUDnAs/+9r9eDujxbeJoTjoVu33gHHcaPfmLQaMhvfbH2GqSe8KUM444epAp1Vl3peVOQfZk3UIqA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", diff --git a/package.json b/package.json index c28416c..ceef582 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@grpc/grpc-js": "^1.8.0", "@grpc/proto-loader": "^0.7.4", "@mobicoop/ddd-library": "file:../../packages/dddlibrary", + "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.2.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e78e2f8..e438c35 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -2,7 +2,8 @@ // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + binaryTargets = ["linux-musl", "debian-openssl-3.0.x"] } datasource db { diff --git a/src/app.di-tokens.ts b/src/app.di-tokens.ts new file mode 100644 index 0000000..61203bb --- /dev/null +++ b/src/app.di-tokens.ts @@ -0,0 +1 @@ +export const MESSAGE_PUBLISHER = Symbol(); diff --git a/src/app.module.ts b/src/app.module.ts index 14e1abd..3fc541b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,20 +1,35 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; +// import { classes } from '@automapper/classes'; +// import { AutomapperModule } from '@automapper/nestjs'; import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { AuthorizationModule } from './modules/authorization/authorization.module'; -import { HealthModule } from './modules/health/health.module'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +// import { AuthorizationModule } from './modules/authorization/authorization.module'; +// import { HealthModule } from './modules/health/health.module'; import { AuthenticationModule } from '@modules/newauthentication/authentication.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; +import { + MessageBrokerModule, + MessageBrokerModuleOptions, +} from '@mobicoop/message-broker-module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), EventEmitterModule.forRoot(), - AutomapperModule.forRoot({ strategyInitializer: classes() }), + MessageBrokerModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async ( + configService: ConfigService, + ): Promise => ({ + uri: configService.get('MESSAGE_BROKER_URI'), + exchange: configService.get('MESSAGE_BROKER_EXCHANGE'), + name: 'auth', + }), + }), + // AutomapperModule.forRoot({ strategyInitializer: classes() }), AuthenticationModule, - AuthorizationModule, - HealthModule, + // AuthorizationModule, + // HealthModule, ], controllers: [], providers: [], diff --git a/src/modules/newauthentication/authentication.mapper.ts b/src/modules/newauthentication/authentication.mapper.ts index d0391ab..92f6e30 100644 --- a/src/modules/newauthentication/authentication.mapper.ts +++ b/src/modules/newauthentication/authentication.mapper.ts @@ -49,6 +49,7 @@ export class AuthenticationMapper createdAt: new Date(record.createdAt), updatedAt: new Date(record.updatedAt), props: { + userId: record.uuid, password: record.password, usernames: record.usernames.map((username: UsernameModel) => ({ name: username.username, diff --git a/src/modules/newauthentication/authentication.module.ts b/src/modules/newauthentication/authentication.module.ts index 83f5270..e636dc5 100644 --- a/src/modules/newauthentication/authentication.module.ts +++ b/src/modules/newauthentication/authentication.module.ts @@ -6,10 +6,20 @@ import { AUTHENTICATION_REPOSITORY } from './authentication.di-tokens'; import { AuthenticationRepository } from './infrastructure/authentication.repository'; import { PrismaService } from './infrastructure/prisma.service'; import { CqrsModule } from '@nestjs/cqrs'; +import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller'; +import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; -const grpcControllers = [CreateAuthenticationGrpcController]; +const grpcControllers = [ + CreateAuthenticationGrpcController, + DeleteAuthenticationGrpcController, +]; -const commandHandlers: Provider[] = [CreateAuthenticationService]; +const commandHandlers: Provider[] = [ + CreateAuthenticationService, + DeleteAuthenticationService, +]; const mappers: Provider[] = [AuthenticationMapper]; @@ -20,12 +30,25 @@ const repositories: Provider[] = [ }, ]; +const messageBrokers: Provider[] = [ + { + provide: MESSAGE_PUBLISHER, + useClass: MessageBrokerPublisher, + }, +]; + const orms: Provider[] = [PrismaService]; @Module({ imports: [CqrsModule], controllers: [...grpcControllers], - providers: [...commandHandlers, ...mappers, ...repositories, ...orms], + providers: [ + ...commandHandlers, + ...mappers, + ...repositories, + ...messageBrokers, + ...orms, + ], exports: [PrismaService, AuthenticationMapper, AUTHENTICATION_REPOSITORY], }) export class AuthenticationModule {} diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts index c5c3c79..5ec8c04 100644 --- a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts +++ b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts @@ -2,11 +2,13 @@ import { Command, CommandProps } from '@mobicoop/ddd-library'; import { Username } from '../types/username'; export class CreateAuthenticationCommand extends Command { + readonly userId: string; readonly password: string; readonly usernames: Username[]; constructor(props: CommandProps) { super(props); + this.userId = props.userId; this.password = props.password; this.usernames = props.usernames; } diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts index d78c895..2ca3188 100644 --- a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts +++ b/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts @@ -3,7 +3,7 @@ import { Inject } from '@nestjs/common'; import { AggregateID, ConflictException, - UniqueConflictException, + UniqueConstraintException, } from '@mobicoop/ddd-library'; import { CreateAuthenticationCommand } from './create-authentication.command'; import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; @@ -22,21 +22,27 @@ export class CreateAuthenticationService implements ICommandHandler { ) {} async execute(command: CreateAuthenticationCommand): Promise { - const authentication = await AuthenticationEntity.create({ - password: command.password, - usernames: command.usernames, - }); - + const authentication: AuthenticationEntity = + await AuthenticationEntity.create({ + userId: command.userId, + password: command.password, + usernames: command.usernames, + }); try { await this.repository.insert(authentication); - return authentication.id; + return authentication.getProps().userId; } catch (error: any) { - console.log('error', error.cause); if (error instanceof ConflictException) { throw new AuthenticationAlreadyExistsException(error); } if ( - error instanceof UniqueConflictException && + error instanceof UniqueConstraintException && + error.message.includes('uuid') + ) { + throw new AuthenticationAlreadyExistsException(error); + } + if ( + error instanceof UniqueConstraintException && error.message.includes('username') ) { throw new UsernameAlreadyExistsException(error); diff --git a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts b/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts new file mode 100644 index 0000000..b0d698d --- /dev/null +++ b/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts @@ -0,0 +1,10 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; + +export class DeleteAuthenticationCommand extends Command { + readonly userId: string; + + constructor(props: CommandProps) { + super(props); + this.userId = props.userId; + } +} diff --git a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts b/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts new file mode 100644 index 0000000..d97f7fa --- /dev/null +++ b/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts @@ -0,0 +1,26 @@ +import { Inject } from '@nestjs/common'; +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { DeleteAuthenticationCommand } from './delete-authentication.command'; +import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; + +@CommandHandler(DeleteAuthenticationCommand) +export class DeleteAuthenticationService implements ICommandHandler { + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly authenticationRepository: AuthenticationRepositoryPort, + ) {} + + async execute(command: DeleteAuthenticationCommand): Promise { + const authentication: AuthenticationEntity = + await this.authenticationRepository.findOneById(command.userId, { + usernames: true, + }); + authentication.delete(); + const deleted: boolean = await this.authenticationRepository.delete( + authentication, + ); + return deleted; + } +} diff --git a/src/modules/newauthentication/core/domain/authentication.entity.ts b/src/modules/newauthentication/core/domain/authentication.entity.ts index 3f44168..d0467c0 100644 --- a/src/modules/newauthentication/core/domain/authentication.entity.ts +++ b/src/modules/newauthentication/core/domain/authentication.entity.ts @@ -1,11 +1,11 @@ import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; -import { v4 } from 'uuid'; import * as bcrypt from 'bcrypt'; import { AuthenticationProps, CreateAuthenticationProps, } from './authentication.types'; import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-events'; +import { AuthenticationDeletedDomainEvent } from './events/authentication-deleted.domain-event'; export class AuthenticationEntity extends AggregateRoot { protected readonly _id: AggregateID; @@ -13,22 +13,30 @@ export class AuthenticationEntity extends AggregateRoot { static create = async ( create: CreateAuthenticationProps, ): Promise => { - const id = v4(); const props: AuthenticationProps = { ...create }; const hash = await bcrypt.hash(props.password, 10); const authentication = new AuthenticationEntity({ - id, + id: props.userId, props: { + userId: props.userId, password: hash, usernames: props.usernames, }, }); authentication.addEvent( - new AuthenticationCreatedDomainEvent({ aggregateId: id }), + new AuthenticationCreatedDomainEvent({ aggregateId: props.userId }), ); return authentication; }; + delete(): void { + this.addEvent( + new AuthenticationDeletedDomainEvent({ + aggregateId: this.id, + }), + ); + } + validate(): void { // entity business rules validation to protect it's invariant before saving entity to a database } diff --git a/src/modules/newauthentication/core/domain/authentication.types.ts b/src/modules/newauthentication/core/domain/authentication.types.ts index afbfd55..81c79ff 100644 --- a/src/modules/newauthentication/core/domain/authentication.types.ts +++ b/src/modules/newauthentication/core/domain/authentication.types.ts @@ -2,12 +2,14 @@ import { UsernameProps } from './username.types'; // All properties that an Authentication has export interface AuthenticationProps { + userId: string; password: string; usernames: UsernameProps[]; } // Properties that are needed for an Authentication creation export interface CreateAuthenticationProps { + userId: string; password: string; usernames: UsernameProps[]; } diff --git a/src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts b/src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts new file mode 100644 index 0000000..fc421ff --- /dev/null +++ b/src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class AuthenticationDeletedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/newauthentication/core/domain/username.entity.ts b/src/modules/newauthentication/core/domain/username.entity.ts deleted file mode 100644 index f5c4454..0000000 --- a/src/modules/newauthentication/core/domain/username.entity.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Entity } from '@mobicoop/ddd-library'; -import { UsernameProps } from './username.types'; - -export class UsernameEntity extends Entity { - protected _id: string; - validate(): void { - throw new Error('Method not implemented.'); - } -} diff --git a/src/modules/newauthentication/core/domain/username.types.ts b/src/modules/newauthentication/core/domain/username.types.ts index a774c26..7b1f1c5 100644 --- a/src/modules/newauthentication/core/domain/username.types.ts +++ b/src/modules/newauthentication/core/domain/username.types.ts @@ -4,12 +4,6 @@ export interface UsernameProps { type: Type; } -// Properties that are needed for a Username creation -export interface CreateUsernameProps { - name: string; - type: Type; -} - export enum Type { EMAIL = 'EMAIL', PHONE = 'PHONE', diff --git a/src/modules/newauthentication/infrastructure/authentication.repository.ts b/src/modules/newauthentication/infrastructure/authentication.repository.ts index f003d3d..be535df 100644 --- a/src/modules/newauthentication/infrastructure/authentication.repository.ts +++ b/src/modules/newauthentication/infrastructure/authentication.repository.ts @@ -1,10 +1,15 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { PrismaRepositoryBase } from '@mobicoop/ddd-library'; +import { + LoggerBase, + MessagePublisherPort, + PrismaRepositoryBase, +} from '@mobicoop/ddd-library'; import { AuthenticationEntity } from '../core/domain/authentication.entity'; import { AuthenticationRepositoryPort } from '../core/application/commands/ports/authentication.repository.port'; import { PrismaService } from './prisma.service'; import { AuthenticationMapper } from '../authentication.mapper'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; export type AuthenticationBaseModel = { uuid: string; @@ -44,13 +49,20 @@ export class AuthenticationRepository prisma: PrismaService, mapper: AuthenticationMapper, eventEmitter: EventEmitter2, + @Inject(MESSAGE_PUBLISHER) + protected readonly messagePublisher: MessagePublisherPort, ) { super( prisma.auth, prisma, mapper, eventEmitter, - new Logger(AuthenticationRepository.name), + new LoggerBase( + AuthenticationRepository.name, + 'logging', + 'auth', + messagePublisher, + ), ); } } diff --git a/src/modules/newauthentication/interface/grpc-controllers/authentication.proto b/src/modules/newauthentication/interface/grpc-controllers/authentication.proto index 36a5af6..87d04f7 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/authentication.proto +++ b/src/modules/newauthentication/interface/grpc-controllers/authentication.proto @@ -9,7 +9,7 @@ service AuthenticationService { rpc UpdatePassword(Password) returns (Id); rpc UpdateUsername(Username) returns (Id); rpc DeleteUsername(Username) returns (Id); - rpc Delete(Id) returns (Empty); + rpc Delete(UserId) returns (Empty); } message AuthenticationByUsernamePassword { @@ -18,17 +18,18 @@ message AuthenticationByUsernamePassword { } message Authentication { - repeated Username usernames = 1; - string password = 2; + string userId = 1; + repeated Username usernames = 2; + string password = 3; } message Password { - string id = 1; + string userId = 1; string password = 2; } message Username { - string id = 1; + string userId = 1; string name = 2; string type = 3; } @@ -37,4 +38,8 @@ message Id { string id = 1; } +message UserId { + string userId = 1; +} + message Empty {} diff --git a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts b/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts index 55bca88..4ad7de5 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts +++ b/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts @@ -22,7 +22,7 @@ export class CreateAuthenticationGrpcController { constructor(private readonly commandBus: CommandBus) {} @GrpcMethod('AuthenticationService', 'Create') - async validate(data: CreateAuthenticationRequestDto): Promise { + async create(data: CreateAuthenticationRequestDto): Promise { try { const aggregateID: AggregateID = await this.commandBus.execute( new CreateAuthenticationCommand(data), diff --git a/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts b/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts new file mode 100644 index 0000000..c66ecd9 --- /dev/null +++ b/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts @@ -0,0 +1,45 @@ +import { + DatabaseErrorException, + NotFoundException, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { DeleteAuthenticationCommand } from '@modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command'; +import { DeleteAuthenticationRequestDto } from './dtos/delete-authentication.request.dto'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class DeleteAuthenticationGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'Delete') + async delete(data: DeleteAuthenticationRequestDto): Promise { + try { + await this.commandBus.execute(new DeleteAuthenticationCommand(data)); + } catch (error: any) { + if (error instanceof NotFoundException) + throw new RpcException({ + code: RpcExceptionCode.NOT_FOUND, + message: error.message, + }); + + if (error instanceof DatabaseErrorException) + throw new RpcException({ + code: RpcExceptionCode.INTERNAL, + message: error.message, + }); + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts b/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts index 91b5765..f4ed5a1 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts +++ b/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts @@ -9,6 +9,10 @@ import { import { UsernameDto } from './username.dto'; export class CreateAuthenticationRequestDto { + @IsString() + @IsNotEmpty() + userId: string; + @Type(() => UsernameDto) @IsArray() @ArrayMinSize(1) diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts b/src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts new file mode 100644 index 0000000..40be7dd --- /dev/null +++ b/src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class DeleteAuthenticationRequestDto { + @IsString() + @IsNotEmpty() + userId: string; +} From 470a93879ee83b15dd0374540495c2c9c2493e3e Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 6 Jul 2023 16:23:18 +0200 Subject: [PATCH 03/23] new authorization --- package.json | 24 ++-- src/app.module.ts | 2 +- src/main.ts | 2 +- .../authentication.di-tokens.ts | 1 + .../authentication.mapper.ts | 1 + .../authentication/authentication.module.ts | 124 +++++++++--------- .../add-usernames/add-username.command.ts | 13 ++ .../add-usernames/add-username.service.ts | 52 ++++++++ .../create-authentication.command.ts | 2 +- .../create-authentication.service.ts | 12 +- .../delete-authentication.command.ts | 0 .../delete-authentication.service.ts | 10 +- .../ports/authentication.repository.port.ts | 2 +- .../ports/username.repository.port.ts | 4 + .../core/application/types/username.ts | 6 + .../core/domain/authentication.entity.ts | 2 +- .../core/domain/authentication.errors.ts | 0 .../core/domain/authentication.types.ts | 0 .../authentication-created.domain-event.ts} | 0 .../authentication-deleted.domain-event.ts | 0 .../events/username-added.domain-event.ts | 7 + .../core/domain/username.entity.ts | 27 ++++ .../core/domain/username.types.ts | 1 + .../authentication.repository.ts | 13 +- .../infrastructure/prisma.service.ts | 0 .../infrastructure/username.repository.ts | 49 +++++++ .../authentication.paginated.response.dto.ts | 0 .../dtos/authentication.response.dto.ts | 0 .../interface/dtos/username.response.dto.ts | 3 + .../add-username.grpc.controller.ts | 49 +++++++ .../grpc-controllers/authentication.proto | 0 .../create-authentication.grpc.controller.ts | 4 +- .../delete-authentication.grpc.controller.ts | 2 +- .../dtos/add-username.request.dto.ts | 8 ++ .../dtos/create-authentication.request.dto.ts | 0 .../dtos/delete-authentication.request.dto.ts | 0 .../grpc-controllers/dtos/username.dto.ts | 16 +++ .../decorators/is-valid-username.decorator.ts | 32 +++++ .../add-username.grpc.controller.spec.ts | 89 +++++++++++++ ...ate-authentication.grpc.controller.spec.ts | 99 ++++++++++++++ ...ete-authentication.grpc.controller.spec.ts | 108 +++++++++++++++ .../is-valid-username.decorator.spec.ts | 57 ++++++++ src/modules/authentication/username.mapper.ts | 49 +++++++ src/modules/database/database.module.ts | 4 +- .../prisma.health-indicator.usecase.ts | 2 +- src/modules/health/health.module.ts | 2 +- .../prisma.health-indicator.usecase.spec.ts | 2 +- .../authentication.module.ts | 54 -------- .../application/commands/types/username.ts | 6 - .../grpc-controllers/dtos/username.dto.ts | 11 -- .../authentication-messager.controller.ts | 0 .../primaries/authentication.controller.ts | 0 .../primaries/authentication.presenter.ts | 0 .../adapters/primaries/authentication.proto | 0 .../adapters/primaries/username.presenter.ts | 0 .../secondaries/authentication.repository.ts | 0 .../adapters/secondaries/messager.ts | 0 .../secondaries/username.repository.ts | 0 .../authentication.module.ts | 66 ++++++++++ .../commands/add-username.command.ts | 0 .../commands/create-authentication.command.ts | 0 .../commands/delete-authentication.command.ts | 0 .../commands/delete-username.command.ts | 0 .../commands/update-password.command.ts | 0 .../commands/update-username.command.ts | 0 .../domain/dtos/add-username.request.ts | 0 .../dtos/create-authentication.request.ts | 0 .../dtos/delete-authentication.request.ts | 0 .../domain/dtos/delete-username.request.ts | 0 .../domain/dtos/type.enum.ts | 0 .../domain/dtos/update-password.request.ts | 0 .../domain/dtos/update-username.request.ts | 0 .../dtos/validate-authentication.request.ts | 0 .../domain/entities/authentication.ts | 0 .../domain/entities/username.ts | 0 .../domain/interfaces/message-broker.ts | 0 .../domain/usecases/add-username.usecase.ts | 0 .../usecases/create-authentication.usecase.ts | 0 .../usecases/delete-authentication.usecase.ts | 0 .../usecases/delete-username.usecase.ts | 0 .../usecases/update-password.usecase.ts | 0 .../usecases/update-username.usecase.ts | 0 .../validate-authentication.usecase.ts | 0 .../mappers/authentication.profile.ts | 0 .../mappers/username.profile.ts | 0 .../queries/validate-authentication.query.ts | 0 .../authentication.repository.spec.ts | 0 .../integration/username.repository.spec.ts | 0 .../tests/unit/add-username.usecase.spec.ts | 0 .../create-authentication.usecase.spec.ts | 0 .../delete-authentication.usecase.spec.ts | 0 .../unit/delete-username.usecase.spec.ts | 0 .../tests/unit/messager.spec.ts | 0 .../unit/update-password.usecase.spec.ts | 0 .../unit/update-username.usecase.spec.ts | 0 .../validate-authentication.usecase.spec.ts | 0 .../unit/rpc-validation-pipe.usecase.spec.ts | 2 +- 97 files changed, 847 insertions(+), 172 deletions(-) rename src/modules/{newauthentication => authentication}/authentication.di-tokens.ts (54%) rename src/modules/{newauthentication => authentication}/authentication.mapper.ts (98%) create mode 100644 src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts create mode 100644 src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts rename src/modules/{newauthentication => authentication}/core/application/commands/create-authentication/create-authentication.command.ts (89%) rename src/modules/{newauthentication => authentication}/core/application/commands/create-authentication/create-authentication.service.ts (74%) rename src/modules/{newauthentication => authentication}/core/application/commands/delete-authentication/delete-authentication.command.ts (100%) rename src/modules/{newauthentication => authentication}/core/application/commands/delete-authentication/delete-authentication.service.ts (65%) rename src/modules/{newauthentication/core/application/commands => authentication/core/application}/ports/authentication.repository.port.ts (57%) create mode 100644 src/modules/authentication/core/application/ports/username.repository.port.ts create mode 100644 src/modules/authentication/core/application/types/username.ts rename src/modules/{newauthentication => authentication}/core/domain/authentication.entity.ts (97%) rename src/modules/{newauthentication => authentication}/core/domain/authentication.errors.ts (100%) rename src/modules/{newauthentication => authentication}/core/domain/authentication.types.ts (100%) rename src/modules/{newauthentication/core/domain/events/authentication-created.domain-events.ts => authentication/core/domain/events/authentication-created.domain-event.ts} (100%) rename src/modules/{newauthentication => authentication}/core/domain/events/authentication-deleted.domain-event.ts (100%) create mode 100644 src/modules/authentication/core/domain/events/username-added.domain-event.ts create mode 100644 src/modules/authentication/core/domain/username.entity.ts rename src/modules/{newauthentication => authentication}/core/domain/username.types.ts (89%) rename src/modules/{newauthentication => authentication}/infrastructure/authentication.repository.ts (87%) rename src/modules/{newauthentication => authentication}/infrastructure/prisma.service.ts (100%) create mode 100644 src/modules/authentication/infrastructure/username.repository.ts rename src/modules/{newauthentication => authentication}/interface/dtos/authentication.paginated.response.dto.ts (100%) rename src/modules/{newauthentication => authentication}/interface/dtos/authentication.response.dto.ts (100%) create mode 100644 src/modules/authentication/interface/dtos/username.response.dto.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts rename src/modules/{newauthentication => authentication}/interface/grpc-controllers/authentication.proto (100%) rename src/modules/{newauthentication => authentication}/interface/grpc-controllers/create-authentication.grpc.controller.ts (82%) rename src/modules/{newauthentication => authentication}/interface/grpc-controllers/delete-authentication.grpc.controller.ts (89%) create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts rename src/modules/{newauthentication => authentication}/interface/grpc-controllers/dtos/create-authentication.request.dto.ts (100%) rename src/modules/{newauthentication => authentication}/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts (100%) create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts create mode 100644 src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts create mode 100644 src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts create mode 100644 src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts create mode 100644 src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts create mode 100644 src/modules/authentication/username.mapper.ts delete mode 100644 src/modules/newauthentication/authentication.module.ts delete mode 100644 src/modules/newauthentication/core/application/commands/types/username.ts delete mode 100644 src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts rename src/modules/{authentication => oldauthentication}/adapters/primaries/authentication-messager.controller.ts (100%) rename src/modules/{authentication => oldauthentication}/adapters/primaries/authentication.controller.ts (100%) rename src/modules/{authentication => oldauthentication}/adapters/primaries/authentication.presenter.ts (100%) rename src/modules/{authentication => oldauthentication}/adapters/primaries/authentication.proto (100%) rename src/modules/{authentication => oldauthentication}/adapters/primaries/username.presenter.ts (100%) rename src/modules/{authentication => oldauthentication}/adapters/secondaries/authentication.repository.ts (100%) rename src/modules/{authentication => oldauthentication}/adapters/secondaries/messager.ts (100%) rename src/modules/{authentication => oldauthentication}/adapters/secondaries/username.repository.ts (100%) create mode 100644 src/modules/oldauthentication/authentication.module.ts rename src/modules/{authentication => oldauthentication}/commands/add-username.command.ts (100%) rename src/modules/{authentication => oldauthentication}/commands/create-authentication.command.ts (100%) rename src/modules/{authentication => oldauthentication}/commands/delete-authentication.command.ts (100%) rename src/modules/{authentication => oldauthentication}/commands/delete-username.command.ts (100%) rename src/modules/{authentication => oldauthentication}/commands/update-password.command.ts (100%) rename src/modules/{authentication => oldauthentication}/commands/update-username.command.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/add-username.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/create-authentication.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/delete-authentication.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/delete-username.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/type.enum.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/update-password.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/update-username.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/dtos/validate-authentication.request.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/entities/authentication.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/entities/username.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/interfaces/message-broker.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/add-username.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/create-authentication.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/delete-authentication.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/delete-username.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/update-password.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/update-username.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/domain/usecases/validate-authentication.usecase.ts (100%) rename src/modules/{authentication => oldauthentication}/mappers/authentication.profile.ts (100%) rename src/modules/{authentication => oldauthentication}/mappers/username.profile.ts (100%) rename src/modules/{authentication => oldauthentication}/queries/validate-authentication.query.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/integration/authentication.repository.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/integration/username.repository.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/add-username.usecase.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/create-authentication.usecase.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/delete-authentication.usecase.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/delete-username.usecase.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/messager.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/update-password.usecase.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/update-username.usecase.spec.ts (100%) rename src/modules/{authentication => oldauthentication}/tests/unit/validate-authentication.usecase.spec.ts (100%) diff --git a/package.json b/package.json index ceef582..44a6478 100644 --- a/package.json +++ b/package.json @@ -91,12 +91,12 @@ "ts" ], "modulePathIgnorePatterns": [ - ".controller.ts", ".module.ts", - ".request.ts", - ".presenter.ts", - ".profile.ts", - ".exception.ts", + ".dto.ts", + ".di-tokens.ts", + ".response.ts", + ".port.ts", + "prisma.service.ts", "main.ts" ], "rootDir": "src", @@ -108,15 +108,19 @@ "**/*.(t|j)s" ], "coveragePathIgnorePatterns": [ - ".controller.ts", ".module.ts", - ".request.ts", - ".presenter.ts", - ".profile.ts", - ".exception.ts", + ".dto.ts", + ".di-tokens.ts", + ".response.ts", + ".port.ts", + "prisma.service.ts", "main.ts" ], "coverageDirectory": "../coverage", + "moduleNameMapper": { + "^@modules(.*)": "/modules/$1", + "^@src(.*)": "$1" + }, "testEnvironment": "node" } } diff --git a/src/app.module.ts b/src/app.module.ts index 3fc541b..adaf99c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,7 +4,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; // import { AuthorizationModule } from './modules/authorization/authorization.module'; // import { HealthModule } from './modules/health/health.module'; -import { AuthenticationModule } from '@modules/newauthentication/authentication.module'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { MessageBrokerModule, diff --git a/src/main.ts b/src/main.ts index 1d38e94..82277f9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,7 +15,7 @@ async function bootstrap() { protoPath: [ join( __dirname, - 'modules/newauthentication/interface/grpc-controllers/authentication.proto', + 'modules/authentication/interface/grpc-controllers/authentication.proto', ), join( __dirname, diff --git a/src/modules/newauthentication/authentication.di-tokens.ts b/src/modules/authentication/authentication.di-tokens.ts similarity index 54% rename from src/modules/newauthentication/authentication.di-tokens.ts rename to src/modules/authentication/authentication.di-tokens.ts index c1afd82..5160269 100644 --- a/src/modules/newauthentication/authentication.di-tokens.ts +++ b/src/modules/authentication/authentication.di-tokens.ts @@ -1 +1,2 @@ export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY'); +export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY'); diff --git a/src/modules/newauthentication/authentication.mapper.ts b/src/modules/authentication/authentication.mapper.ts similarity index 98% rename from src/modules/newauthentication/authentication.mapper.ts rename to src/modules/authentication/authentication.mapper.ts index 92f6e30..367fd07 100644 --- a/src/modules/newauthentication/authentication.mapper.ts +++ b/src/modules/authentication/authentication.mapper.ts @@ -52,6 +52,7 @@ export class AuthenticationMapper userId: record.uuid, password: record.password, usernames: record.usernames.map((username: UsernameModel) => ({ + userId: record.uuid, name: username.username, type: Type[username.type], })), diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 3b7ed79..8a9401d 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -1,66 +1,72 @@ -import { Module } from '@nestjs/common'; +import { Module, Provider } from '@nestjs/common'; +import { CreateAuthenticationGrpcController } from './interface/grpc-controllers/create-authentication.grpc.controller'; +import { CreateAuthenticationService } from './core/application/commands/create-authentication/create-authentication.service'; +import { AuthenticationMapper } from './authentication.mapper'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from './authentication.di-tokens'; +import { AuthenticationRepository } from './infrastructure/authentication.repository'; +import { PrismaService } from './infrastructure/prisma.service'; import { CqrsModule } from '@nestjs/cqrs'; -import { DatabaseModule } from '../database/database.module'; -import { AuthenticationController } from './adapters/primaries/authentication.controller'; -import { CreateAuthenticationUseCase } from './domain/usecases/create-authentication.usecase'; -import { ValidateAuthenticationUseCase } from './domain/usecases/validate-authentication.usecase'; -import { AuthenticationProfile } from './mappers/authentication.profile'; -import { AuthenticationRepository } from './adapters/secondaries/authentication.repository'; -import { UpdateUsernameUseCase } from './domain/usecases/update-username.usecase'; -import { UsernameProfile } from './mappers/username.profile'; -import { AddUsernameUseCase } from './domain/usecases/add-username.usecase'; -import { UpdatePasswordUseCase } from './domain/usecases/update-password.usecase'; -import { DeleteUsernameUseCase } from './domain/usecases/delete-username.usecase'; -import { DeleteAuthenticationUseCase } from './domain/usecases/delete-authentication.usecase'; -import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { AuthenticationMessagerController } from './adapters/primaries/authentication-messager.controller'; -import { Messager } from './adapters/secondaries/messager'; +import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller'; +import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { UsernameRepository } from './infrastructure/username.repository'; +import { UsernameMapper } from './username.mapper'; +import { AddUsernameGrpcController } from './interface/grpc-controllers/add-username.grpc.controller'; +import { AddUsernameService } from './core/application/commands/add-usernames/add-username.service'; + +const grpcControllers = [ + CreateAuthenticationGrpcController, + DeleteAuthenticationGrpcController, + AddUsernameGrpcController, +]; + +const commandHandlers: Provider[] = [ + CreateAuthenticationService, + DeleteAuthenticationService, + AddUsernameService, +]; + +const mappers: Provider[] = [AuthenticationMapper, UsernameMapper]; + +const repositories: Provider[] = [ + { + provide: AUTHENTICATION_REPOSITORY, + useClass: AuthenticationRepository, + }, + { + provide: USERNAME_REPOSITORY, + useClass: UsernameRepository, + }, +]; + +const messageBrokers: Provider[] = [ + { + provide: MESSAGE_PUBLISHER, + useClass: MessageBrokerPublisher, + }, +]; + +const orms: Provider[] = [PrismaService]; @Module({ - imports: [ - DatabaseModule, - CqrsModule, - RabbitMQModule.forRootAsync(RabbitMQModule, { - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - exchanges: [ - { - name: configService.get('RMQ_EXCHANGE'), - type: 'topic', - }, - ], - handlers: { - userUpdate: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'user.update', - }, - userDelete: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'user.delete', - }, - }, - uri: configService.get('RMQ_URI'), - connectionInitOptions: { wait: false }, - enableControllerDiscovery: true, - }), - inject: [ConfigService], - }), - ], - controllers: [AuthenticationController, AuthenticationMessagerController], + imports: [CqrsModule], + controllers: [...grpcControllers], providers: [ - AuthenticationProfile, - UsernameProfile, - AuthenticationRepository, - Messager, - ValidateAuthenticationUseCase, - CreateAuthenticationUseCase, - AddUsernameUseCase, - UpdateUsernameUseCase, - UpdatePasswordUseCase, - DeleteUsernameUseCase, - DeleteAuthenticationUseCase, + ...commandHandlers, + ...mappers, + ...repositories, + ...messageBrokers, + ...orms, + ], + exports: [ + PrismaService, + AuthenticationMapper, + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, ], - exports: [], }) export class AuthenticationModule {} diff --git a/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts b/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts new file mode 100644 index 0000000..3eaf1fb --- /dev/null +++ b/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts @@ -0,0 +1,13 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; +import { Username } from '../../types/username'; + +export class AddUsernameCommand extends Command { + readonly userId: string; + readonly username: Username; + + constructor(props: CommandProps) { + super(props); + this.userId = props.userId; + this.username = props.username; + } +} diff --git a/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts b/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts new file mode 100644 index 0000000..e290e51 --- /dev/null +++ b/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts @@ -0,0 +1,52 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { Inject } from '@nestjs/common'; +import { + AggregateID, + ConflictException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { AddUsernameCommand } from './add-username.command'; +import { UsernameRepositoryPort } from '../../ports/username.repository.port'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; + +@CommandHandler(AddUsernameCommand) +export class AddUsernameService implements ICommandHandler { + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly authenticationRepository: AuthenticationRepositoryPort, + @Inject(USERNAME_REPOSITORY) + private readonly usernameRepository: UsernameRepositoryPort, + ) {} + + async execute(command: AddUsernameCommand): Promise { + await this.authenticationRepository.findOneById(command.userId, { + usernames: true, + }); + try { + const newUsername = await UsernameEntity.create({ + name: command.username.name, + userId: command.userId, + type: command.username.type, + }); + await this.usernameRepository.insert(newUsername); + return newUsername.id; + } catch (error: any) { + if (error instanceof ConflictException) { + throw new UsernameAlreadyExistsException(error); + } + if ( + error instanceof UniqueConstraintException && + error.message.includes('username') + ) { + throw new UsernameAlreadyExistsException(error); + } + throw error; + } + } +} diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.command.ts similarity index 89% rename from src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts rename to src/modules/authentication/core/application/commands/create-authentication/create-authentication.command.ts index 5ec8c04..e60ffe1 100644 --- a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts +++ b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.command.ts @@ -1,5 +1,5 @@ import { Command, CommandProps } from '@mobicoop/ddd-library'; -import { Username } from '../types/username'; +import { Username } from '../../types/username'; export class CreateAuthenticationCommand extends Command { readonly userId: string; diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts similarity index 74% rename from src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts rename to src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts index 2ca3188..3ff15f1 100644 --- a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts +++ b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts @@ -6,19 +6,19 @@ import { UniqueConstraintException, } from '@mobicoop/ddd-library'; import { CreateAuthenticationCommand } from './create-authentication.command'; -import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; -import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port'; -import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; import { AuthenticationAlreadyExistsException, UsernameAlreadyExistsException, -} from '@modules/newauthentication/core/domain/authentication.errors'; +} from '@modules/authentication/core/domain/authentication.errors'; @CommandHandler(CreateAuthenticationCommand) export class CreateAuthenticationService implements ICommandHandler { constructor( @Inject(AUTHENTICATION_REPOSITORY) - private readonly repository: AuthenticationRepositoryPort, + private readonly authenticationRepository: AuthenticationRepositoryPort, ) {} async execute(command: CreateAuthenticationCommand): Promise { @@ -29,7 +29,7 @@ export class CreateAuthenticationService implements ICommandHandler { usernames: command.usernames, }); try { - await this.repository.insert(authentication); + await this.authenticationRepository.insert(authentication); return authentication.getProps().userId; } catch (error: any) { if (error instanceof ConflictException) { diff --git a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts b/src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.command.ts similarity index 100% rename from src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts rename to src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.command.ts diff --git a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts b/src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.service.ts similarity index 65% rename from src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts rename to src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.service.ts index d97f7fa..f365e22 100644 --- a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts +++ b/src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.service.ts @@ -1,9 +1,9 @@ import { Inject } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { DeleteAuthenticationCommand } from './delete-authentication.command'; -import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; -import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port'; -import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; @CommandHandler(DeleteAuthenticationCommand) export class DeleteAuthenticationService implements ICommandHandler { @@ -18,9 +18,9 @@ export class DeleteAuthenticationService implements ICommandHandler { usernames: true, }); authentication.delete(); - const deleted: boolean = await this.authenticationRepository.delete( + const isDeleted: boolean = await this.authenticationRepository.delete( authentication, ); - return deleted; + return isDeleted; } } diff --git a/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts b/src/modules/authentication/core/application/ports/authentication.repository.port.ts similarity index 57% rename from src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts rename to src/modules/authentication/core/application/ports/authentication.repository.port.ts index 3b8ad0a..775db19 100644 --- a/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts +++ b/src/modules/authentication/core/application/ports/authentication.repository.port.ts @@ -1,4 +1,4 @@ import { RepositoryPort } from '@mobicoop/ddd-library'; -import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; export type AuthenticationRepositoryPort = RepositoryPort; diff --git a/src/modules/authentication/core/application/ports/username.repository.port.ts b/src/modules/authentication/core/application/ports/username.repository.port.ts new file mode 100644 index 0000000..1e11ac4 --- /dev/null +++ b/src/modules/authentication/core/application/ports/username.repository.port.ts @@ -0,0 +1,4 @@ +import { RepositoryPort } from '@mobicoop/ddd-library'; +import { UsernameEntity } from '../../domain/username.entity'; + +export type UsernameRepositoryPort = RepositoryPort; diff --git a/src/modules/authentication/core/application/types/username.ts b/src/modules/authentication/core/application/types/username.ts new file mode 100644 index 0000000..aa123cc --- /dev/null +++ b/src/modules/authentication/core/application/types/username.ts @@ -0,0 +1,6 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; + +export type Username = { + name: string; + type: Type; +}; diff --git a/src/modules/newauthentication/core/domain/authentication.entity.ts b/src/modules/authentication/core/domain/authentication.entity.ts similarity index 97% rename from src/modules/newauthentication/core/domain/authentication.entity.ts rename to src/modules/authentication/core/domain/authentication.entity.ts index d0467c0..81ed651 100644 --- a/src/modules/newauthentication/core/domain/authentication.entity.ts +++ b/src/modules/authentication/core/domain/authentication.entity.ts @@ -4,7 +4,7 @@ import { AuthenticationProps, CreateAuthenticationProps, } from './authentication.types'; -import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-events'; +import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-event'; import { AuthenticationDeletedDomainEvent } from './events/authentication-deleted.domain-event'; export class AuthenticationEntity extends AggregateRoot { diff --git a/src/modules/newauthentication/core/domain/authentication.errors.ts b/src/modules/authentication/core/domain/authentication.errors.ts similarity index 100% rename from src/modules/newauthentication/core/domain/authentication.errors.ts rename to src/modules/authentication/core/domain/authentication.errors.ts diff --git a/src/modules/newauthentication/core/domain/authentication.types.ts b/src/modules/authentication/core/domain/authentication.types.ts similarity index 100% rename from src/modules/newauthentication/core/domain/authentication.types.ts rename to src/modules/authentication/core/domain/authentication.types.ts diff --git a/src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts b/src/modules/authentication/core/domain/events/authentication-created.domain-event.ts similarity index 100% rename from src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts rename to src/modules/authentication/core/domain/events/authentication-created.domain-event.ts diff --git a/src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts b/src/modules/authentication/core/domain/events/authentication-deleted.domain-event.ts similarity index 100% rename from src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts rename to src/modules/authentication/core/domain/events/authentication-deleted.domain-event.ts diff --git a/src/modules/authentication/core/domain/events/username-added.domain-event.ts b/src/modules/authentication/core/domain/events/username-added.domain-event.ts new file mode 100644 index 0000000..aea5849 --- /dev/null +++ b/src/modules/authentication/core/domain/events/username-added.domain-event.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class UsernameAddedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/authentication/core/domain/username.entity.ts b/src/modules/authentication/core/domain/username.entity.ts new file mode 100644 index 0000000..5fd83fa --- /dev/null +++ b/src/modules/authentication/core/domain/username.entity.ts @@ -0,0 +1,27 @@ +import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library'; +import { UsernameProps } from './username.types'; +import { UsernameAddedDomainEvent } from './events/username-added.domain-event'; + +export class UsernameEntity extends AggregateRoot { + protected readonly _id: AggregateID; + + static create = async (create: UsernameProps): Promise => { + const props: UsernameProps = { ...create }; + const username = new UsernameEntity({ + id: props.name, + props: { + name: props.name, + userId: props.userId, + type: props.type, + }, + }); + username.addEvent( + new UsernameAddedDomainEvent({ aggregateId: props.name }), + ); + return username; + }; + + validate(): void { + // entity business rules validation to protect it's invariant before saving entity to a database + } +} diff --git a/src/modules/newauthentication/core/domain/username.types.ts b/src/modules/authentication/core/domain/username.types.ts similarity index 89% rename from src/modules/newauthentication/core/domain/username.types.ts rename to src/modules/authentication/core/domain/username.types.ts index 7b1f1c5..382ce83 100644 --- a/src/modules/newauthentication/core/domain/username.types.ts +++ b/src/modules/authentication/core/domain/username.types.ts @@ -1,6 +1,7 @@ // All properties that a Username has export interface UsernameProps { name: string; + userId?: string; type: Type; } diff --git a/src/modules/newauthentication/infrastructure/authentication.repository.ts b/src/modules/authentication/infrastructure/authentication.repository.ts similarity index 87% rename from src/modules/newauthentication/infrastructure/authentication.repository.ts rename to src/modules/authentication/infrastructure/authentication.repository.ts index be535df..f176613 100644 --- a/src/modules/newauthentication/infrastructure/authentication.repository.ts +++ b/src/modules/authentication/infrastructure/authentication.repository.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { LoggerBase, @@ -6,7 +6,7 @@ import { PrismaRepositoryBase, } from '@mobicoop/ddd-library'; import { AuthenticationEntity } from '../core/domain/authentication.entity'; -import { AuthenticationRepositoryPort } from '../core/application/commands/ports/authentication.repository.port'; +import { AuthenticationRepositoryPort } from '../core/application/ports/authentication.repository.port'; import { PrismaService } from './prisma.service'; import { AuthenticationMapper } from '../authentication.mapper'; import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; @@ -57,12 +57,11 @@ export class AuthenticationRepository prisma, mapper, eventEmitter, - new LoggerBase( - AuthenticationRepository.name, - 'logging', - 'auth', + new LoggerBase({ + logger: new Logger(AuthenticationRepository.name), + domain: 'auth', messagePublisher, - ), + }), ); } } diff --git a/src/modules/newauthentication/infrastructure/prisma.service.ts b/src/modules/authentication/infrastructure/prisma.service.ts similarity index 100% rename from src/modules/newauthentication/infrastructure/prisma.service.ts rename to src/modules/authentication/infrastructure/prisma.service.ts diff --git a/src/modules/authentication/infrastructure/username.repository.ts b/src/modules/authentication/infrastructure/username.repository.ts new file mode 100644 index 0000000..1ee3e68 --- /dev/null +++ b/src/modules/authentication/infrastructure/username.repository.ts @@ -0,0 +1,49 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { + LoggerBase, + MessagePublisherPort, + PrismaRepositoryBase, +} from '@mobicoop/ddd-library'; +import { PrismaService } from './prisma.service'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { UsernameEntity } from '../core/domain/username.entity'; +import { UsernameRepositoryPort } from '../core/application/ports/username.repository.port'; +import { UsernameMapper } from '../username.mapper'; + +export type UsernameModel = { + username: string; + authUuid: string; + type: string; + createdAt: Date; + updatedAt: Date; +}; + +/** + * Repository is used for retrieving/saving domain entities + * */ +@Injectable() +export class UsernameRepository + extends PrismaRepositoryBase + implements UsernameRepositoryPort +{ + constructor( + prisma: PrismaService, + mapper: UsernameMapper, + eventEmitter: EventEmitter2, + @Inject(MESSAGE_PUBLISHER) + protected readonly messagePublisher: MessagePublisherPort, + ) { + super( + prisma.username, + prisma, + mapper, + eventEmitter, + new LoggerBase({ + logger: new Logger(UsernameRepository.name), + domain: 'auth', + messagePublisher, + }), + ); + } +} diff --git a/src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts b/src/modules/authentication/interface/dtos/authentication.paginated.response.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts rename to src/modules/authentication/interface/dtos/authentication.paginated.response.dto.ts diff --git a/src/modules/newauthentication/interface/dtos/authentication.response.dto.ts b/src/modules/authentication/interface/dtos/authentication.response.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/dtos/authentication.response.dto.ts rename to src/modules/authentication/interface/dtos/authentication.response.dto.ts diff --git a/src/modules/authentication/interface/dtos/username.response.dto.ts b/src/modules/authentication/interface/dtos/username.response.dto.ts new file mode 100644 index 0000000..0212b77 --- /dev/null +++ b/src/modules/authentication/interface/dtos/username.response.dto.ts @@ -0,0 +1,3 @@ +import { ResponseBase } from '@mobicoop/ddd-library'; + +export class UsernameResponseDto extends ResponseBase {} diff --git a/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts new file mode 100644 index 0000000..a3d51c6 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts @@ -0,0 +1,49 @@ +import { + AggregateID, + IdResponse, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { AddUsernameRequestDto } from './dtos/add-username.request.dto'; +import { AddUsernameCommand } from '@modules/authentication/core/application/commands/add-usernames/add-username.command'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class AddUsernameGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'AddUsername') + async addUsername(data: AddUsernameRequestDto): Promise { + try { + const aggregateID: AggregateID = await this.commandBus.execute( + new AddUsernameCommand({ + userId: data.userId, + username: { + name: data.name, + type: data.type, + }, + }), + ); + return new IdResponse(aggregateID); + } catch (error: any) { + if (error instanceof UsernameAlreadyExistsException) + throw new RpcException({ + code: RpcExceptionCode.ALREADY_EXISTS, + message: error.message, + }); + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/authentication.proto b/src/modules/authentication/interface/grpc-controllers/authentication.proto similarity index 100% rename from src/modules/newauthentication/interface/grpc-controllers/authentication.proto rename to src/modules/authentication/interface/grpc-controllers/authentication.proto diff --git a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller.ts similarity index 82% rename from src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts rename to src/modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller.ts index 4ad7de5..4abae17 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts +++ b/src/modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller.ts @@ -8,8 +8,8 @@ import { Controller, UsePipes } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { CreateAuthenticationRequestDto } from './dtos/create-authentication.request.dto'; -import { CreateAuthenticationCommand } from '@modules/newauthentication/core/application/commands/create-authentication/create-authentication.command'; -import { AuthenticationAlreadyExistsException } from '@modules/newauthentication/core/domain/authentication.errors'; +import { CreateAuthenticationCommand } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.command'; +import { AuthenticationAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; @UsePipes( new RpcValidationPipe({ diff --git a/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts similarity index 89% rename from src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts rename to src/modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts index c66ecd9..6769e39 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts +++ b/src/modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts @@ -7,7 +7,7 @@ import { import { Controller, UsePipes } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; -import { DeleteAuthenticationCommand } from '@modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command'; +import { DeleteAuthenticationCommand } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.command'; import { DeleteAuthenticationRequestDto } from './dtos/delete-authentication.request.dto'; @UsePipes( diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts new file mode 100644 index 0000000..74de9f6 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts @@ -0,0 +1,8 @@ +import { IsNotEmpty, IsString } from 'class-validator'; +import { UsernameDto } from './username.dto'; + +export class AddUsernameRequestDto extends UsernameDto { + @IsString() + @IsNotEmpty() + userId: string; +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts rename to src/modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts rename to src/modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts new file mode 100644 index 0000000..b744d75 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts @@ -0,0 +1,16 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { IsValidUsername } from './validators/decorators/is-valid-username.decorator'; + +export class UsernameDto { + @IsString() + @IsNotEmpty() + @IsValidUsername({ + message: 'Invalid username', + }) + name: string; + + @IsEnum(Type) + @IsNotEmpty() + type: Type; +} diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts b/src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts new file mode 100644 index 0000000..5785b39 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts @@ -0,0 +1,32 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; +import { + registerDecorator, + ValidationOptions, + ValidationArguments, + isEmail, + isPhoneNumber, +} from 'class-validator'; + +export function IsValidUsername(validationOptions?: ValidationOptions) { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'isValidUsername', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any, args: ValidationArguments) { + const usernameType: Type = args.object['type']; + switch (usernameType) { + case Type.PHONE: + return isPhoneNumber(value); + case Type.EMAIL: + return isEmail(value); + default: + return false; + } + }, + }, + }); + }; +} diff --git a/src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts new file mode 100644 index 0000000..b4786b9 --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts @@ -0,0 +1,89 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { AddUsernameGrpcController } from '@modules/authentication/interface/grpc-controllers/add-username.grpc.controller'; +import { AddUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const addUsernameRequest: AddUsernameRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', + name: 'john.doe@email.com', + type: Type.EMAIL, +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => 'john.doe@email.com') + .mockImplementationOnce(() => { + throw new UsernameAlreadyExistsException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Add Username Grpc Controller', () => { + let addUsernameGrpcController: AddUsernameGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + AddUsernameGrpcController, + ], + }).compile(); + + addUsernameGrpcController = module.get( + AddUsernameGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(addUsernameGrpcController).toBeDefined(); + }); + + it('should add a new username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + const result: IdResponse = await addUsernameGrpcController.addUsername( + addUsernameRequest, + ); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('john.doe@email.com'); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if username already exists', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await addUsernameGrpcController.addUsername(addUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await addUsernameGrpcController.addUsername(addUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts new file mode 100644 index 0000000..e45942a --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts @@ -0,0 +1,99 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { AuthenticationAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { CreateAuthenticationGrpcController } from '@modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller'; +import { CreateAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const createAuthenticationRequest: CreateAuthenticationRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', + password: 'John123', + usernames: [ + { + name: 'john.doe@email.com', + type: Type.EMAIL, + }, + ], +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => '78153e03-4861-4f58-a705-88526efee53b') + .mockImplementationOnce(() => { + throw new AuthenticationAlreadyExistsException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Create Authentication Grpc Controller', () => { + let createAuthenticationGrpcController: CreateAuthenticationGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + CreateAuthenticationGrpcController, + ], + }).compile(); + + createAuthenticationGrpcController = + module.get( + CreateAuthenticationGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(createAuthenticationGrpcController).toBeDefined(); + }); + + it('should create a new authentication', async () => { + jest.spyOn(mockCommandBus, 'execute'); + const result: IdResponse = await createAuthenticationGrpcController.create( + createAuthenticationRequest, + ); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('78153e03-4861-4f58-a705-88526efee53b'); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if authentication already exists', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await createAuthenticationGrpcController.create( + createAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await createAuthenticationGrpcController.create( + createAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts new file mode 100644 index 0000000..8f070aa --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts @@ -0,0 +1,108 @@ +import { + DatabaseErrorException, + NotFoundException, +} from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { DeleteAuthenticationGrpcController } from '@modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller'; +import { DeleteAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const deleteAuthenticationRequest: DeleteAuthenticationRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new NotFoundException(); + }) + .mockImplementationOnce(() => { + throw new DatabaseErrorException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Delete Authentication Grpc Controller', () => { + let deleteAuthenticationGrpcController: DeleteAuthenticationGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + DeleteAuthenticationGrpcController, + ], + }).compile(); + + deleteAuthenticationGrpcController = + module.get( + DeleteAuthenticationGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(deleteAuthenticationGrpcController).toBeDefined(); + }); + + it('should create a new authentication', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if authentication does not exist', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.NOT_FOUND); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if a database error occurs', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.INTERNAL); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts b/src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts new file mode 100644 index 0000000..3b6d59f --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts @@ -0,0 +1,57 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; +import { IsValidUsername } from '@modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator'; +import { Validator } from 'class-validator'; + +describe('Username decorator', () => { + class MyClass { + @IsValidUsername({ + message: 'Invalid username', + }) + name: string; + + type: Type; + } + it('should return a property decorator has a function', () => { + const isValidUsername = IsValidUsername(); + expect(typeof isValidUsername).toBe('function'); + }); + it('should validate a valid phone username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = '+33611223344'; + myClassInstance.type = Type.PHONE; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(0); + }); + it('should validate a valid email username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = 'john.doe@email.com'; + myClassInstance.type = Type.EMAIL; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(0); + }); + it('should not validate an invalid phone username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = '11223344'; + myClassInstance.type = Type.PHONE; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(1); + }); + it('should not validate an invalid email username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = 'john.doe.email.com'; + myClassInstance.type = Type.EMAIL; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(1); + }); + it('should not validate if type is not set', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = 'john.doe@email.com'; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(1); + }); +}); diff --git a/src/modules/authentication/username.mapper.ts b/src/modules/authentication/username.mapper.ts new file mode 100644 index 0000000..b31a906 --- /dev/null +++ b/src/modules/authentication/username.mapper.ts @@ -0,0 +1,49 @@ +import { Mapper } from '@mobicoop/ddd-library'; +import { Injectable } from '@nestjs/common'; +import { Type } from './core/domain/username.types'; +import { UsernameEntity } from './core/domain/username.entity'; +import { UsernameModel } from './infrastructure/username.repository'; +import { UsernameResponseDto } from './interface/dtos/username.response.dto'; + +/** + * Mapper constructs objects that are used in different layers: + * Record is an object that is stored in a database, + * Entity is an object that is used in application domain layer, + * and a ResponseDTO is an object returned to a user (usually as json). + */ + +@Injectable() +export class UsernameMapper + implements + Mapper +{ + toPersistence = (entity: UsernameEntity): UsernameModel => { + const copy = entity.getProps(); + const record: UsernameModel = { + authUuid: copy.userId, + username: copy.name, + type: copy.type, + createdAt: copy.createdAt, + updatedAt: copy.updatedAt, + }; + return record; + }; + + toDomain = (record: UsernameModel): UsernameEntity => { + const entity = new UsernameEntity({ + id: record.authUuid, + createdAt: new Date(record.createdAt), + updatedAt: new Date(record.updatedAt), + props: { + name: record.username, + type: Type[record.type], + }, + }); + return entity; + }; + + toResponse = (entity: UsernameEntity): UsernameResponseDto => { + const response = new UsernameResponseDto(entity); + return response; + }; +} diff --git a/src/modules/database/database.module.ts b/src/modules/database/database.module.ts index 2240bdb..40d3fc7 100644 --- a/src/modules/database/database.module.ts +++ b/src/modules/database/database.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AuthenticationRepository } from '../authentication/adapters/secondaries/authentication.repository'; -import { UsernameRepository } from '../authentication/adapters/secondaries/username.repository'; +import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository'; +import { UsernameRepository } from '../oldauthentication/adapters/secondaries/username.repository'; import { PrismaService } from './adapters/secondaries/prisma-service'; @Module({ diff --git a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts b/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts index 35b4b32..2d77a7b 100644 --- a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts +++ b/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts @@ -4,7 +4,7 @@ import { HealthIndicator, HealthIndicatorResult, } from '@nestjs/terminus'; -import { AuthenticationRepository } from '../../../authentication/adapters/secondaries/authentication.repository'; +import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository'; @Injectable() export class PrismaHealthIndicatorUseCase extends HealthIndicator { diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index c8ec6ee..374fe2f 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { HealthServerController } from './adapters/primaries/health-server.controller'; import { PrismaHealthIndicatorUseCase } from './domain/usecases/prisma.health-indicator.usecase'; -import { AuthenticationRepository } from '../authentication/adapters/secondaries/authentication.repository'; +import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository'; import { DatabaseModule } from '../database/database.module'; import { HealthController } from './adapters/primaries/health.controller'; import { TerminusModule } from '@nestjs/terminus'; diff --git a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts index 27d21d6..d7d9971 100644 --- a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts +++ b/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; -import { AuthenticationRepository } from '../../../authentication/adapters/secondaries/authentication.repository'; +import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository'; import { PrismaClientKnownRequestError } from '@prisma/client/runtime'; import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; diff --git a/src/modules/newauthentication/authentication.module.ts b/src/modules/newauthentication/authentication.module.ts deleted file mode 100644 index e636dc5..0000000 --- a/src/modules/newauthentication/authentication.module.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Module, Provider } from '@nestjs/common'; -import { CreateAuthenticationGrpcController } from './interface/grpc-controllers/create-authentication.grpc.controller'; -import { CreateAuthenticationService } from './core/application/commands/create-authentication/create-authentication.service'; -import { AuthenticationMapper } from './authentication.mapper'; -import { AUTHENTICATION_REPOSITORY } from './authentication.di-tokens'; -import { AuthenticationRepository } from './infrastructure/authentication.repository'; -import { PrismaService } from './infrastructure/prisma.service'; -import { CqrsModule } from '@nestjs/cqrs'; -import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller'; -import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service'; -import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; -import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; - -const grpcControllers = [ - CreateAuthenticationGrpcController, - DeleteAuthenticationGrpcController, -]; - -const commandHandlers: Provider[] = [ - CreateAuthenticationService, - DeleteAuthenticationService, -]; - -const mappers: Provider[] = [AuthenticationMapper]; - -const repositories: Provider[] = [ - { - provide: AUTHENTICATION_REPOSITORY, - useClass: AuthenticationRepository, - }, -]; - -const messageBrokers: Provider[] = [ - { - provide: MESSAGE_PUBLISHER, - useClass: MessageBrokerPublisher, - }, -]; - -const orms: Provider[] = [PrismaService]; - -@Module({ - imports: [CqrsModule], - controllers: [...grpcControllers], - providers: [ - ...commandHandlers, - ...mappers, - ...repositories, - ...messageBrokers, - ...orms, - ], - exports: [PrismaService, AuthenticationMapper, AUTHENTICATION_REPOSITORY], -}) -export class AuthenticationModule {} diff --git a/src/modules/newauthentication/core/application/commands/types/username.ts b/src/modules/newauthentication/core/application/commands/types/username.ts deleted file mode 100644 index b8c4452..0000000 --- a/src/modules/newauthentication/core/application/commands/types/username.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Type } from '@modules/newauthentication/core/domain/username.types'; - -export type Username = { - name: string; - type: Type; -}; diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts b/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts deleted file mode 100644 index 665ee21..0000000 --- a/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Type } from '@modules/newauthentication/core/domain/username.types'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; - -export class UsernameDto { - @IsString() - name: string; - - @IsEnum(Type) - @IsNotEmpty() - type: Type; -} diff --git a/src/modules/authentication/adapters/primaries/authentication-messager.controller.ts b/src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication-messager.controller.ts rename to src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts diff --git a/src/modules/authentication/adapters/primaries/authentication.controller.ts b/src/modules/oldauthentication/adapters/primaries/authentication.controller.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication.controller.ts rename to src/modules/oldauthentication/adapters/primaries/authentication.controller.ts diff --git a/src/modules/authentication/adapters/primaries/authentication.presenter.ts b/src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication.presenter.ts rename to src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts diff --git a/src/modules/authentication/adapters/primaries/authentication.proto b/src/modules/oldauthentication/adapters/primaries/authentication.proto similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication.proto rename to src/modules/oldauthentication/adapters/primaries/authentication.proto diff --git a/src/modules/authentication/adapters/primaries/username.presenter.ts b/src/modules/oldauthentication/adapters/primaries/username.presenter.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/username.presenter.ts rename to src/modules/oldauthentication/adapters/primaries/username.presenter.ts diff --git a/src/modules/authentication/adapters/secondaries/authentication.repository.ts b/src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts similarity index 100% rename from src/modules/authentication/adapters/secondaries/authentication.repository.ts rename to src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts diff --git a/src/modules/authentication/adapters/secondaries/messager.ts b/src/modules/oldauthentication/adapters/secondaries/messager.ts similarity index 100% rename from src/modules/authentication/adapters/secondaries/messager.ts rename to src/modules/oldauthentication/adapters/secondaries/messager.ts diff --git a/src/modules/authentication/adapters/secondaries/username.repository.ts b/src/modules/oldauthentication/adapters/secondaries/username.repository.ts similarity index 100% rename from src/modules/authentication/adapters/secondaries/username.repository.ts rename to src/modules/oldauthentication/adapters/secondaries/username.repository.ts diff --git a/src/modules/oldauthentication/authentication.module.ts b/src/modules/oldauthentication/authentication.module.ts new file mode 100644 index 0000000..3b7ed79 --- /dev/null +++ b/src/modules/oldauthentication/authentication.module.ts @@ -0,0 +1,66 @@ +import { Module } from '@nestjs/common'; +import { CqrsModule } from '@nestjs/cqrs'; +import { DatabaseModule } from '../database/database.module'; +import { AuthenticationController } from './adapters/primaries/authentication.controller'; +import { CreateAuthenticationUseCase } from './domain/usecases/create-authentication.usecase'; +import { ValidateAuthenticationUseCase } from './domain/usecases/validate-authentication.usecase'; +import { AuthenticationProfile } from './mappers/authentication.profile'; +import { AuthenticationRepository } from './adapters/secondaries/authentication.repository'; +import { UpdateUsernameUseCase } from './domain/usecases/update-username.usecase'; +import { UsernameProfile } from './mappers/username.profile'; +import { AddUsernameUseCase } from './domain/usecases/add-username.usecase'; +import { UpdatePasswordUseCase } from './domain/usecases/update-password.usecase'; +import { DeleteUsernameUseCase } from './domain/usecases/delete-username.usecase'; +import { DeleteAuthenticationUseCase } from './domain/usecases/delete-authentication.usecase'; +import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { AuthenticationMessagerController } from './adapters/primaries/authentication-messager.controller'; +import { Messager } from './adapters/secondaries/messager'; + +@Module({ + imports: [ + DatabaseModule, + CqrsModule, + RabbitMQModule.forRootAsync(RabbitMQModule, { + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + exchanges: [ + { + name: configService.get('RMQ_EXCHANGE'), + type: 'topic', + }, + ], + handlers: { + userUpdate: { + exchange: configService.get('RMQ_EXCHANGE'), + routingKey: 'user.update', + }, + userDelete: { + exchange: configService.get('RMQ_EXCHANGE'), + routingKey: 'user.delete', + }, + }, + uri: configService.get('RMQ_URI'), + connectionInitOptions: { wait: false }, + enableControllerDiscovery: true, + }), + inject: [ConfigService], + }), + ], + controllers: [AuthenticationController, AuthenticationMessagerController], + providers: [ + AuthenticationProfile, + UsernameProfile, + AuthenticationRepository, + Messager, + ValidateAuthenticationUseCase, + CreateAuthenticationUseCase, + AddUsernameUseCase, + UpdateUsernameUseCase, + UpdatePasswordUseCase, + DeleteUsernameUseCase, + DeleteAuthenticationUseCase, + ], + exports: [], +}) +export class AuthenticationModule {} diff --git a/src/modules/authentication/commands/add-username.command.ts b/src/modules/oldauthentication/commands/add-username.command.ts similarity index 100% rename from src/modules/authentication/commands/add-username.command.ts rename to src/modules/oldauthentication/commands/add-username.command.ts diff --git a/src/modules/authentication/commands/create-authentication.command.ts b/src/modules/oldauthentication/commands/create-authentication.command.ts similarity index 100% rename from src/modules/authentication/commands/create-authentication.command.ts rename to src/modules/oldauthentication/commands/create-authentication.command.ts diff --git a/src/modules/authentication/commands/delete-authentication.command.ts b/src/modules/oldauthentication/commands/delete-authentication.command.ts similarity index 100% rename from src/modules/authentication/commands/delete-authentication.command.ts rename to src/modules/oldauthentication/commands/delete-authentication.command.ts diff --git a/src/modules/authentication/commands/delete-username.command.ts b/src/modules/oldauthentication/commands/delete-username.command.ts similarity index 100% rename from src/modules/authentication/commands/delete-username.command.ts rename to src/modules/oldauthentication/commands/delete-username.command.ts diff --git a/src/modules/authentication/commands/update-password.command.ts b/src/modules/oldauthentication/commands/update-password.command.ts similarity index 100% rename from src/modules/authentication/commands/update-password.command.ts rename to src/modules/oldauthentication/commands/update-password.command.ts diff --git a/src/modules/authentication/commands/update-username.command.ts b/src/modules/oldauthentication/commands/update-username.command.ts similarity index 100% rename from src/modules/authentication/commands/update-username.command.ts rename to src/modules/oldauthentication/commands/update-username.command.ts diff --git a/src/modules/authentication/domain/dtos/add-username.request.ts b/src/modules/oldauthentication/domain/dtos/add-username.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/add-username.request.ts rename to src/modules/oldauthentication/domain/dtos/add-username.request.ts diff --git a/src/modules/authentication/domain/dtos/create-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/create-authentication.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/create-authentication.request.ts rename to src/modules/oldauthentication/domain/dtos/create-authentication.request.ts diff --git a/src/modules/authentication/domain/dtos/delete-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/delete-authentication.request.ts rename to src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts diff --git a/src/modules/authentication/domain/dtos/delete-username.request.ts b/src/modules/oldauthentication/domain/dtos/delete-username.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/delete-username.request.ts rename to src/modules/oldauthentication/domain/dtos/delete-username.request.ts diff --git a/src/modules/authentication/domain/dtos/type.enum.ts b/src/modules/oldauthentication/domain/dtos/type.enum.ts similarity index 100% rename from src/modules/authentication/domain/dtos/type.enum.ts rename to src/modules/oldauthentication/domain/dtos/type.enum.ts diff --git a/src/modules/authentication/domain/dtos/update-password.request.ts b/src/modules/oldauthentication/domain/dtos/update-password.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/update-password.request.ts rename to src/modules/oldauthentication/domain/dtos/update-password.request.ts diff --git a/src/modules/authentication/domain/dtos/update-username.request.ts b/src/modules/oldauthentication/domain/dtos/update-username.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/update-username.request.ts rename to src/modules/oldauthentication/domain/dtos/update-username.request.ts diff --git a/src/modules/authentication/domain/dtos/validate-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/validate-authentication.request.ts rename to src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts diff --git a/src/modules/authentication/domain/entities/authentication.ts b/src/modules/oldauthentication/domain/entities/authentication.ts similarity index 100% rename from src/modules/authentication/domain/entities/authentication.ts rename to src/modules/oldauthentication/domain/entities/authentication.ts diff --git a/src/modules/authentication/domain/entities/username.ts b/src/modules/oldauthentication/domain/entities/username.ts similarity index 100% rename from src/modules/authentication/domain/entities/username.ts rename to src/modules/oldauthentication/domain/entities/username.ts diff --git a/src/modules/authentication/domain/interfaces/message-broker.ts b/src/modules/oldauthentication/domain/interfaces/message-broker.ts similarity index 100% rename from src/modules/authentication/domain/interfaces/message-broker.ts rename to src/modules/oldauthentication/domain/interfaces/message-broker.ts diff --git a/src/modules/authentication/domain/usecases/add-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/add-username.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/add-username.usecase.ts rename to src/modules/oldauthentication/domain/usecases/add-username.usecase.ts diff --git a/src/modules/authentication/domain/usecases/create-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/create-authentication.usecase.ts rename to src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts diff --git a/src/modules/authentication/domain/usecases/delete-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/delete-authentication.usecase.ts rename to src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts diff --git a/src/modules/authentication/domain/usecases/delete-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/delete-username.usecase.ts rename to src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts diff --git a/src/modules/authentication/domain/usecases/update-password.usecase.ts b/src/modules/oldauthentication/domain/usecases/update-password.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/update-password.usecase.ts rename to src/modules/oldauthentication/domain/usecases/update-password.usecase.ts diff --git a/src/modules/authentication/domain/usecases/update-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/update-username.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/update-username.usecase.ts rename to src/modules/oldauthentication/domain/usecases/update-username.usecase.ts diff --git a/src/modules/authentication/domain/usecases/validate-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/validate-authentication.usecase.ts rename to src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts diff --git a/src/modules/authentication/mappers/authentication.profile.ts b/src/modules/oldauthentication/mappers/authentication.profile.ts similarity index 100% rename from src/modules/authentication/mappers/authentication.profile.ts rename to src/modules/oldauthentication/mappers/authentication.profile.ts diff --git a/src/modules/authentication/mappers/username.profile.ts b/src/modules/oldauthentication/mappers/username.profile.ts similarity index 100% rename from src/modules/authentication/mappers/username.profile.ts rename to src/modules/oldauthentication/mappers/username.profile.ts diff --git a/src/modules/authentication/queries/validate-authentication.query.ts b/src/modules/oldauthentication/queries/validate-authentication.query.ts similarity index 100% rename from src/modules/authentication/queries/validate-authentication.query.ts rename to src/modules/oldauthentication/queries/validate-authentication.query.ts diff --git a/src/modules/authentication/tests/integration/authentication.repository.spec.ts b/src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts similarity index 100% rename from src/modules/authentication/tests/integration/authentication.repository.spec.ts rename to src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts diff --git a/src/modules/authentication/tests/integration/username.repository.spec.ts b/src/modules/oldauthentication/tests/integration/username.repository.spec.ts similarity index 100% rename from src/modules/authentication/tests/integration/username.repository.spec.ts rename to src/modules/oldauthentication/tests/integration/username.repository.spec.ts diff --git a/src/modules/authentication/tests/unit/add-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/add-username.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/create-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/create-authentication.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/delete-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/delete-authentication.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/delete-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/delete-username.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/messager.spec.ts b/src/modules/oldauthentication/tests/unit/messager.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/messager.spec.ts rename to src/modules/oldauthentication/tests/unit/messager.spec.ts diff --git a/src/modules/authentication/tests/unit/update-password.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/update-password.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/update-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/update-username.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/validate-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/validate-authentication.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts diff --git a/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts b/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts index b02a9b7..3f5af01 100644 --- a/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts +++ b/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts @@ -1,5 +1,5 @@ import { ArgumentMetadata } from '@nestjs/common'; -import { ValidateAuthenticationRequest } from '../../../modules/authentication/domain/dtos/validate-authentication.request'; +import { ValidateAuthenticationRequest } from '../../../modules/oldauthentication/domain/dtos/validate-authentication.request'; import { RpcValidationPipe } from '../../pipes/rpc.validation-pipe'; describe('RpcValidationPipe', () => { From b23f8f9ade5af21e4e8b44a1a9b993364180c588 Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 6 Jul 2023 17:38:57 +0200 Subject: [PATCH 04/23] tests for infractructure, domain entities and create authentication service --- .../core/domain/username.entity.ts | 6 +- .../core/domain/username.types.ts | 7 +- .../unit/core/authentication.entity.spec.ts | 61 ++++++++ .../create-authentication.service.spec.ts | 131 ++++++++++++++++++ .../tests/unit/core/username.entity.spec.ts | 21 +++ .../authentication.repository.spec.ts | 37 +++++ .../username.repository.spec.ts | 36 +++++ 7 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 src/modules/authentication/tests/unit/core/authentication.entity.spec.ts create mode 100644 src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts create mode 100644 src/modules/authentication/tests/unit/core/username.entity.spec.ts create mode 100644 src/modules/authentication/tests/unit/infrastructure/authentication.repository.spec.ts create mode 100644 src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts diff --git a/src/modules/authentication/core/domain/username.entity.ts b/src/modules/authentication/core/domain/username.entity.ts index 5fd83fa..d00806e 100644 --- a/src/modules/authentication/core/domain/username.entity.ts +++ b/src/modules/authentication/core/domain/username.entity.ts @@ -1,11 +1,13 @@ import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library'; -import { UsernameProps } from './username.types'; +import { CreateUsernameProps, UsernameProps } from './username.types'; import { UsernameAddedDomainEvent } from './events/username-added.domain-event'; export class UsernameEntity extends AggregateRoot { protected readonly _id: AggregateID; - static create = async (create: UsernameProps): Promise => { + static create = async ( + create: CreateUsernameProps, + ): Promise => { const props: UsernameProps = { ...create }; const username = new UsernameEntity({ id: props.name, diff --git a/src/modules/authentication/core/domain/username.types.ts b/src/modules/authentication/core/domain/username.types.ts index 382ce83..6f671b0 100644 --- a/src/modules/authentication/core/domain/username.types.ts +++ b/src/modules/authentication/core/domain/username.types.ts @@ -1,10 +1,15 @@ -// All properties that a Username has export interface UsernameProps { name: string; userId?: string; type: Type; } +export interface CreateUsernameProps { + name: string; + userId: string; + type: Type; +} + export enum Type { EMAIL = 'EMAIL', PHONE = 'PHONE', diff --git a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts new file mode 100644 index 0000000..e4e2655 --- /dev/null +++ b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts @@ -0,0 +1,61 @@ +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; +import { CreateAuthenticationProps } from '@modules/authentication/core/domain/authentication.types'; +import { AuthenticationDeletedDomainEvent } from '@modules/authentication/core/domain/events/authentication-deleted.domain-event'; +import { Type } from '@modules/authentication/core/domain/username.types'; + +const createAuthenticationProps: CreateAuthenticationProps = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + password: 'somePassword', + usernames: [ + { + type: Type.EMAIL, + name: 'john.doe@email.com', + }, + ], +}; + +const createAuthenticationPropsWith2Usernames: CreateAuthenticationProps = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + password: 'somePassword', + usernames: [ + { + type: Type.EMAIL, + name: 'john.doe@email.com', + }, + { + type: Type.PHONE, + name: '+33611223344', + }, + ], +}; + +describe('Authentication entity create', () => { + it('should create a new authentication entity', async () => { + const authenticationEntity: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + expect(authenticationEntity.id).toBe( + '165192d4-398a-4469-a16b-98c02cc6f531', + ); + expect(authenticationEntity.domainEvents.length).toBe(1); + }); + it('should create a new authentication entity with 2 usernames', async () => { + const authenticationEntity: AuthenticationEntity = + await AuthenticationEntity.create( + createAuthenticationPropsWith2Usernames, + ); + expect(authenticationEntity.id).toBe( + '165192d4-398a-4469-a16b-98c02cc6f531', + ); + expect(authenticationEntity.getProps().usernames.length).toBe(2); + expect(authenticationEntity.domainEvents.length).toBe(1); + }); + it('should delete an authentication entity', async () => { + const authenticationEntity: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + authenticationEntity.delete(); + expect(authenticationEntity.domainEvents.length).toBe(2); + expect(authenticationEntity.domainEvents[1]).toBeInstanceOf( + AuthenticationDeletedDomainEvent, + ); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts b/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts new file mode 100644 index 0000000..b4b1d25 --- /dev/null +++ b/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts @@ -0,0 +1,131 @@ +import { + AggregateID, + ConflictException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { CreateAuthenticationCommand } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.command'; +import { CreateAuthenticationService } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.service'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; +import { + AuthenticationAlreadyExistsException, + UsernameAlreadyExistsException, +} from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { CreateAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto'; +import { Test, TestingModule } from '@nestjs/testing'; + +const createAuthenticationRequest: CreateAuthenticationRequestDto = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + password: 'somePassword', + usernames: [ + { + name: 'john.doe@email.com', + type: Type.EMAIL, + }, + ], +}; + +const mockAuthenticationRepository = { + insert: jest + .fn() + .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new ConflictException('already exists'); + }) + .mockImplementationOnce(() => { + throw new UniqueConstraintException('uuid'); + }) + .mockImplementationOnce(() => { + throw new UniqueConstraintException('username'); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('create-authentication.service', () => { + let createAuthenticationService: CreateAuthenticationService; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: AUTHENTICATION_REPOSITORY, + useValue: mockAuthenticationRepository, + }, + CreateAuthenticationService, + ], + }).compile(); + + createAuthenticationService = module.get( + CreateAuthenticationService, + ); + }); + + it('should be defined', () => { + expect(createAuthenticationService).toBeDefined(); + }); + + describe('execution', () => { + const createAdCommand = new CreateAuthenticationCommand( + createAuthenticationRequest, + ); + it('should create a new authentication', async () => { + AuthenticationEntity.create = jest.fn().mockReturnValue({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + })), + }); + const result: AggregateID = await createAuthenticationService.execute( + createAdCommand, + ); + expect(result).toBe('165192d4-398a-4469-a16b-98c02cc6f531'); + }); + it('should throw a dedicated exception if Authentication already exists', async () => { + AuthenticationEntity.create = jest.fn().mockReturnValue({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + })), + }); + await expect( + createAuthenticationService.execute(createAdCommand), + ).rejects.toBeInstanceOf(AuthenticationAlreadyExistsException); + }); + it('should throw a dedicated exception if uuid already exists', async () => { + AuthenticationEntity.create = jest.fn().mockReturnValue({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + })), + }); + await expect( + createAuthenticationService.execute(createAdCommand), + ).rejects.toBeInstanceOf(AuthenticationAlreadyExistsException); + }); + it('should throw a dedicated exception if username already exists', async () => { + AuthenticationEntity.create = jest.fn().mockReturnValue({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + })), + }); + await expect( + createAuthenticationService.execute(createAdCommand), + ).rejects.toBeInstanceOf(UsernameAlreadyExistsException); + }); + it('should throw an error if something bad happens', async () => { + AuthenticationEntity.create = jest.fn().mockReturnValue({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + })), + }); + await expect( + createAuthenticationService.execute(createAdCommand), + ).rejects.toBeInstanceOf(Error); + }); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/username.entity.spec.ts b/src/modules/authentication/tests/unit/core/username.entity.spec.ts new file mode 100644 index 0000000..2cb19c5 --- /dev/null +++ b/src/modules/authentication/tests/unit/core/username.entity.spec.ts @@ -0,0 +1,21 @@ +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; +import { + CreateUsernameProps, + Type, +} from '@modules/authentication/core/domain/username.types'; + +const createUsernameProps: CreateUsernameProps = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + type: Type.EMAIL, + name: 'john.doe@email.com', +}; + +describe('Username entity create', () => { + it('should create a new username entity', async () => { + const usernameEntity: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + expect(usernameEntity.id).toBe('john.doe@email.com'); + expect(usernameEntity.domainEvents.length).toBe(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/infrastructure/authentication.repository.spec.ts b/src/modules/authentication/tests/unit/infrastructure/authentication.repository.spec.ts new file mode 100644 index 0000000..c7aec80 --- /dev/null +++ b/src/modules/authentication/tests/unit/infrastructure/authentication.repository.spec.ts @@ -0,0 +1,37 @@ +import { AuthenticationMapper } from '@modules/authentication/authentication.mapper'; +import { AuthenticationRepository } from '@modules/authentication/infrastructure/authentication.repository'; +import { PrismaService } from '@modules/authentication/infrastructure/prisma.service'; +import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; +import { Test, TestingModule } from '@nestjs/testing'; + +const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), +}; + +describe('Authentication repository', () => { + let prismaService: PrismaService; + let authenticationMapper: AuthenticationMapper; + let eventEmitter: EventEmitter2; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [EventEmitterModule.forRoot()], + providers: [PrismaService, AuthenticationMapper], + }).compile(); + + prismaService = module.get(PrismaService); + authenticationMapper = + module.get(AuthenticationMapper); + eventEmitter = module.get(EventEmitter2); + }); + it('should be defined', () => { + expect( + new AuthenticationRepository( + prismaService, + authenticationMapper, + eventEmitter, + mockMessagePublisher, + ), + ).toBeDefined(); + }); +}); diff --git a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts new file mode 100644 index 0000000..16f9124 --- /dev/null +++ b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts @@ -0,0 +1,36 @@ +import { UsernameMapper } from '@modules/authentication/username.mapper'; +import { PrismaService } from '@modules/authentication/infrastructure/prisma.service'; +import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; +import { Test, TestingModule } from '@nestjs/testing'; +import { UsernameRepository } from '@modules/authentication/infrastructure/username.repository'; + +const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), +}; + +describe('Username repository', () => { + let prismaService: PrismaService; + let usernameMapper: UsernameMapper; + let eventEmitter: EventEmitter2; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [EventEmitterModule.forRoot()], + providers: [PrismaService, UsernameMapper], + }).compile(); + + prismaService = module.get(PrismaService); + usernameMapper = module.get(UsernameMapper); + eventEmitter = module.get(EventEmitter2); + }); + it('should be defined', () => { + expect( + new UsernameRepository( + prismaService, + usernameMapper, + eventEmitter, + mockMessagePublisher, + ), + ).toBeDefined(); + }); +}); From 39924d12570161247d8b8471e2a51713ca218518 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Jul 2023 09:10:07 +0200 Subject: [PATCH 05/23] add username tests --- .../authentication/authentication.module.ts | 2 +- .../add-username.command.ts | 0 .../add-username.service.ts | 0 .../add-username.grpc.controller.ts | 2 +- .../unit/core/add-username.service.spec.ts | 106 ++++++++++++++++++ .../create-authentication.service.spec.ts | 50 +++------ 6 files changed, 121 insertions(+), 39 deletions(-) rename src/modules/authentication/core/application/commands/{add-usernames => add-username}/add-username.command.ts (100%) rename src/modules/authentication/core/application/commands/{add-usernames => add-username}/add-username.service.ts (100%) create mode 100644 src/modules/authentication/tests/unit/core/add-username.service.spec.ts diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 8a9401d..dc0c776 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -16,7 +16,7 @@ import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; import { UsernameRepository } from './infrastructure/username.repository'; import { UsernameMapper } from './username.mapper'; import { AddUsernameGrpcController } from './interface/grpc-controllers/add-username.grpc.controller'; -import { AddUsernameService } from './core/application/commands/add-usernames/add-username.service'; +import { AddUsernameService } from './core/application/commands/add-username/add-username.service'; const grpcControllers = [ CreateAuthenticationGrpcController, diff --git a/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts b/src/modules/authentication/core/application/commands/add-username/add-username.command.ts similarity index 100% rename from src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts rename to src/modules/authentication/core/application/commands/add-username/add-username.command.ts diff --git a/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts b/src/modules/authentication/core/application/commands/add-username/add-username.service.ts similarity index 100% rename from src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts rename to src/modules/authentication/core/application/commands/add-username/add-username.service.ts diff --git a/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts index a3d51c6..bc41a4e 100644 --- a/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts +++ b/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts @@ -9,7 +9,7 @@ import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; import { AddUsernameRequestDto } from './dtos/add-username.request.dto'; -import { AddUsernameCommand } from '@modules/authentication/core/application/commands/add-usernames/add-username.command'; +import { AddUsernameCommand } from '@modules/authentication/core/application/commands/add-username/add-username.command'; @UsePipes( new RpcValidationPipe({ diff --git a/src/modules/authentication/tests/unit/core/add-username.service.spec.ts b/src/modules/authentication/tests/unit/core/add-username.service.spec.ts new file mode 100644 index 0000000..8e9411a --- /dev/null +++ b/src/modules/authentication/tests/unit/core/add-username.service.spec.ts @@ -0,0 +1,106 @@ +import { + AggregateID, + ConflictException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; +import { AddUsernameCommand } from '@modules/authentication/core/application/commands/add-username/add-username.command'; +import { AddUsernameService } from '@modules/authentication/core/application/commands/add-username/add-username.service'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { AddUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto'; +import { Test, TestingModule } from '@nestjs/testing'; + +const addUsernameRequest: AddUsernameRequestDto = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + name: 'john.doe@email.com', + type: Type.EMAIL, +}; + +const mockAuthenticationRepository = { + findOneById: jest.fn().mockImplementationOnce(() => ({})), +}; + +const mockUsernameRepository = { + insert: jest + .fn() + .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new ConflictException('already exists'); + }) + .mockImplementationOnce(() => { + throw new UniqueConstraintException('username'); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Add Username Service', () => { + let addUsernameService: AddUsernameService; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: AUTHENTICATION_REPOSITORY, + useValue: mockAuthenticationRepository, + }, + { + provide: USERNAME_REPOSITORY, + useValue: mockUsernameRepository, + }, + AddUsernameService, + ], + }).compile(); + + addUsernameService = module.get(AddUsernameService); + }); + + it('should be defined', () => { + expect(addUsernameService).toBeDefined(); + }); + + describe('execution', () => { + const addUsernameCommand = new AddUsernameCommand({ + userId: addUsernameRequest.userId, + username: { + name: addUsernameRequest.name, + type: addUsernameRequest.type, + }, + }); + UsernameEntity.create = jest.fn().mockReturnValue({ + id: 'john.doe@email.com', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + name: 'john.doe@email.com', + type: Type.EMAIL, + })), + }); + it('should add a new username', async () => { + const result: AggregateID = await addUsernameService.execute( + addUsernameCommand, + ); + expect(result).toBe('john.doe@email.com'); + }); + it('should throw a dedicated exception if username already exists for this type', async () => { + await expect( + addUsernameService.execute(addUsernameCommand), + ).rejects.toBeInstanceOf(UsernameAlreadyExistsException); + }); + it('should throw a dedicated exception if username already exists for this name', async () => { + await expect( + addUsernameService.execute(addUsernameCommand), + ).rejects.toBeInstanceOf(UsernameAlreadyExistsException); + }); + it('should throw an error if something bad happens', async () => { + await expect( + addUsernameService.execute(addUsernameCommand), + ).rejects.toBeInstanceOf(Error); + }); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts b/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts index b4b1d25..b4df3d5 100644 --- a/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts +++ b/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts @@ -44,7 +44,7 @@ const mockAuthenticationRepository = { }), }; -describe('create-authentication.service', () => { +describe('Create Authentication Service', () => { let createAuthenticationService: CreateAuthenticationService; beforeAll(async () => { @@ -68,63 +68,39 @@ describe('create-authentication.service', () => { }); describe('execution', () => { - const createAdCommand = new CreateAuthenticationCommand( + const createAuthenticationCommand = new CreateAuthenticationCommand( createAuthenticationRequest, ); + AuthenticationEntity.create = jest.fn().mockReturnValue({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + })), + }); it('should create a new authentication', async () => { - AuthenticationEntity.create = jest.fn().mockReturnValue({ - id: '165192d4-398a-4469-a16b-98c02cc6f531', - getProps: jest.fn().mockImplementation(() => ({ - userId: '165192d4-398a-4469-a16b-98c02cc6f531', - })), - }); const result: AggregateID = await createAuthenticationService.execute( - createAdCommand, + createAuthenticationCommand, ); expect(result).toBe('165192d4-398a-4469-a16b-98c02cc6f531'); }); it('should throw a dedicated exception if Authentication already exists', async () => { - AuthenticationEntity.create = jest.fn().mockReturnValue({ - id: '165192d4-398a-4469-a16b-98c02cc6f531', - getProps: jest.fn().mockImplementation(() => ({ - userId: '165192d4-398a-4469-a16b-98c02cc6f531', - })), - }); await expect( - createAuthenticationService.execute(createAdCommand), + createAuthenticationService.execute(createAuthenticationCommand), ).rejects.toBeInstanceOf(AuthenticationAlreadyExistsException); }); it('should throw a dedicated exception if uuid already exists', async () => { - AuthenticationEntity.create = jest.fn().mockReturnValue({ - id: '165192d4-398a-4469-a16b-98c02cc6f531', - getProps: jest.fn().mockImplementation(() => ({ - userId: '165192d4-398a-4469-a16b-98c02cc6f531', - })), - }); await expect( - createAuthenticationService.execute(createAdCommand), + createAuthenticationService.execute(createAuthenticationCommand), ).rejects.toBeInstanceOf(AuthenticationAlreadyExistsException); }); it('should throw a dedicated exception if username already exists', async () => { - AuthenticationEntity.create = jest.fn().mockReturnValue({ - id: '165192d4-398a-4469-a16b-98c02cc6f531', - getProps: jest.fn().mockImplementation(() => ({ - userId: '165192d4-398a-4469-a16b-98c02cc6f531', - })), - }); await expect( - createAuthenticationService.execute(createAdCommand), + createAuthenticationService.execute(createAuthenticationCommand), ).rejects.toBeInstanceOf(UsernameAlreadyExistsException); }); it('should throw an error if something bad happens', async () => { - AuthenticationEntity.create = jest.fn().mockReturnValue({ - id: '165192d4-398a-4469-a16b-98c02cc6f531', - getProps: jest.fn().mockImplementation(() => ({ - userId: '165192d4-398a-4469-a16b-98c02cc6f531', - })), - }); await expect( - createAuthenticationService.execute(createAdCommand), + createAuthenticationService.execute(createAuthenticationCommand), ).rejects.toBeInstanceOf(Error); }); }); From e123f190c6788b5bdf3ae6eaf69b5d580d86a531 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Jul 2023 10:11:15 +0200 Subject: [PATCH 06/23] delete authentication service tests --- .../delete-authentication.service.spec.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/modules/authentication/tests/unit/core/delete-authentication.service.spec.ts diff --git a/src/modules/authentication/tests/unit/core/delete-authentication.service.spec.ts b/src/modules/authentication/tests/unit/core/delete-authentication.service.spec.ts new file mode 100644 index 0000000..5c8824c --- /dev/null +++ b/src/modules/authentication/tests/unit/core/delete-authentication.service.spec.ts @@ -0,0 +1,54 @@ +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { DeleteAuthenticationCommand } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.command'; +import { DeleteAuthenticationService } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.service'; +import { DeleteAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto'; +import { Test, TestingModule } from '@nestjs/testing'; + +const deleteAuthenticationRequest: DeleteAuthenticationRequestDto = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', +}; + +const mockAuthenticationEntity = { + delete: jest.fn(), +}; + +const mockAuthenticationRepository = { + findOneById: jest.fn().mockImplementation(() => mockAuthenticationEntity), + delete: jest.fn().mockImplementationOnce(() => true), +}; + +describe('Delete Authentication Service', () => { + let deleteAuthenticationService: DeleteAuthenticationService; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: AUTHENTICATION_REPOSITORY, + useValue: mockAuthenticationRepository, + }, + DeleteAuthenticationService, + ], + }).compile(); + + deleteAuthenticationService = module.get( + DeleteAuthenticationService, + ); + }); + + it('should be defined', () => { + expect(deleteAuthenticationService).toBeDefined(); + }); + + describe('execution', () => { + const deleteAuthenticationCommand = new DeleteAuthenticationCommand( + deleteAuthenticationRequest, + ); + it('should delete an authentication', async () => { + const result: boolean = await deleteAuthenticationService.execute( + deleteAuthenticationCommand, + ); + expect(result).toBeTruthy(); + }); + }); +}); From 8e1bfe68d5694113ea5ad766df9ddc5c22c43658 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Jul 2023 10:32:35 +0200 Subject: [PATCH 07/23] authentication mapper tests --- .../tests/unit/authentication.mapper.spec.ts | 82 +++++++++++++++++++ .../unit/core/authentication.entity.spec.ts | 1 + 2 files changed, 83 insertions(+) create mode 100644 src/modules/authentication/tests/unit/authentication.mapper.spec.ts diff --git a/src/modules/authentication/tests/unit/authentication.mapper.spec.ts b/src/modules/authentication/tests/unit/authentication.mapper.spec.ts new file mode 100644 index 0000000..02046c8 --- /dev/null +++ b/src/modules/authentication/tests/unit/authentication.mapper.spec.ts @@ -0,0 +1,82 @@ +import { AuthenticationMapper } from '@modules/authentication/authentication.mapper'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { + AuthenticationReadModel, + AuthenticationWriteModel, +} from '@modules/authentication/infrastructure/authentication.repository'; +import { AuthenticationResponseDto } from '@modules/authentication/interface/dtos/authentication.response.dto'; +import { Test } from '@nestjs/testing'; + +const now = new Date('2023-06-21 06:00:00'); +const authenticationEntity: AuthenticationEntity = new AuthenticationEntity({ + id: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e', + props: { + userId: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e', + password: 'somePassword', + usernames: [ + { + name: 'john.doe@email.com', + type: Type.EMAIL, + }, + { + name: '+33611223344', + type: Type.PHONE, + }, + ], + }, + createdAt: now, + updatedAt: now, +}); +const authenticationReadModel: AuthenticationReadModel = { + uuid: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e', + password: '$2b$10$mpkLb3LGyQGJoPaWNBAGReSQHflXPWMjw5RXpebz8EVIMfHJYZ0Fu', + usernames: [ + { + username: 'john.doe@email.com', + type: Type.EMAIL, + }, + { + username: '+33611223344', + type: Type.PHONE, + }, + ], + createdAt: now, + updatedAt: now, +}; + +describe('Authentication Mapper', () => { + let authenticationMapper: AuthenticationMapper; + + beforeAll(async () => { + const module = await Test.createTestingModule({ + providers: [AuthenticationMapper], + }).compile(); + authenticationMapper = + module.get(AuthenticationMapper); + }); + + it('should be defined', () => { + expect(authenticationMapper).toBeDefined(); + }); + + it('should map domain entity to persistence data', async () => { + const mapped: AuthenticationWriteModel = + authenticationMapper.toPersistence(authenticationEntity); + expect(mapped.usernames.create[0].username).toBe('john.doe@email.com'); + expect(mapped.usernames.create[1].username).toBe('+33611223344'); + }); + + it('should map persisted data to domain entity', async () => { + const mapped: AuthenticationEntity = authenticationMapper.toDomain( + authenticationReadModel, + ); + expect(mapped.getProps().usernames[1].name).toBe('+33611223344'); + }); + + it('should map domain entity to response', async () => { + const mapped: AuthenticationResponseDto = + authenticationMapper.toResponse(authenticationEntity); + expect(mapped.id).toBe('7ca2490b-d04d-4ac5-8d6c-5c416fab922e'); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts index e4e2655..5f3de63 100644 --- a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts @@ -36,6 +36,7 @@ describe('Authentication entity create', () => { expect(authenticationEntity.id).toBe( '165192d4-398a-4469-a16b-98c02cc6f531', ); + expect(authenticationEntity.getProps().password.length).toBe(60); expect(authenticationEntity.domainEvents.length).toBe(1); }); it('should create a new authentication entity with 2 usernames', async () => { From a95caefaf258533442afff83601dc9701b4ca0cf Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Jul 2023 10:39:55 +0200 Subject: [PATCH 08/23] username mapper tests --- .../tests/unit/username.mapper.spec.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/modules/authentication/tests/unit/username.mapper.spec.ts diff --git a/src/modules/authentication/tests/unit/username.mapper.spec.ts b/src/modules/authentication/tests/unit/username.mapper.spec.ts new file mode 100644 index 0000000..c7250e3 --- /dev/null +++ b/src/modules/authentication/tests/unit/username.mapper.spec.ts @@ -0,0 +1,57 @@ +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { UsernameModel } from '@modules/authentication/infrastructure/username.repository'; +import { UsernameResponseDto } from '@modules/authentication/interface/dtos/username.response.dto'; +import { UsernameMapper } from '@modules/authentication/username.mapper'; +import { Test } from '@nestjs/testing'; + +const now = new Date('2023-06-21 06:00:00'); +const usernameEntity: UsernameEntity = new UsernameEntity({ + id: 'john.doe@email.com', + props: { + userId: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e', + name: 'john.doe@email.com', + type: Type.EMAIL, + }, + createdAt: now, + updatedAt: now, +}); +const usernameModel: UsernameModel = { + authUuid: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e', + username: 'john.doe@email.com', + type: Type.EMAIL, + createdAt: now, + updatedAt: now, +}; + +describe('Username Mapper', () => { + let usernameMapper: UsernameMapper; + + beforeAll(async () => { + const module = await Test.createTestingModule({ + providers: [UsernameMapper], + }).compile(); + usernameMapper = module.get(UsernameMapper); + }); + + it('should be defined', () => { + expect(usernameMapper).toBeDefined(); + }); + + it('should map domain entity to persistence data', async () => { + const mapped: UsernameModel = usernameMapper.toPersistence(usernameEntity); + expect(mapped.username).toBe('john.doe@email.com'); + expect(mapped.type).toBe(Type.EMAIL); + }); + + it('should map persisted data to domain entity', async () => { + const mapped: UsernameEntity = usernameMapper.toDomain(usernameModel); + expect(mapped.getProps().name).toBe('john.doe@email.com'); + }); + + it('should map domain entity to response', async () => { + const mapped: UsernameResponseDto = + usernameMapper.toResponse(usernameEntity); + expect(mapped.id).toBe('john.doe@email.com'); + }); +}); From 28c6ca0f63c35b0069a86d985d67f46a8f1068a7 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Jul 2023 11:11:36 +0200 Subject: [PATCH 09/23] delete username usecase --- .../authentication/authentication.module.ts | 4 + .../delete-username.command.ts | 10 ++ .../delete-username.service.ts | 23 +++++ .../events/username-deleted.domain-event.ts | 7 ++ .../core/domain/username.entity.ts | 9 ++ .../delete-username.grpc.controller.ts | 45 +++++++++ .../dtos/delete-username.request.dto.ts | 7 ++ .../unit/core/authentication.entity.spec.ts | 2 + .../unit/core/delete-username.service.spec.ts | 54 ++++++++++ .../tests/unit/core/username.entity.spec.ts | 13 +++ ...ete-authentication.grpc.controller.spec.ts | 2 +- .../delete-username.grpc.controller.spec.ts | 99 +++++++++++++++++++ 12 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/modules/authentication/core/application/commands/delete-username/delete-username.command.ts create mode 100644 src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts create mode 100644 src/modules/authentication/core/domain/events/username-deleted.domain-event.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto.ts create mode 100644 src/modules/authentication/tests/unit/core/delete-username.service.spec.ts create mode 100644 src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index dc0c776..5084013 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -17,17 +17,21 @@ import { UsernameRepository } from './infrastructure/username.repository'; import { UsernameMapper } from './username.mapper'; import { AddUsernameGrpcController } from './interface/grpc-controllers/add-username.grpc.controller'; import { AddUsernameService } from './core/application/commands/add-username/add-username.service'; +import { DeleteUsernameGrpcController } from './interface/grpc-controllers/delete-username.grpc.controller'; +import { DeleteUsernameService } from './core/application/commands/delete-username/delete-username.service'; const grpcControllers = [ CreateAuthenticationGrpcController, DeleteAuthenticationGrpcController, AddUsernameGrpcController, + DeleteUsernameGrpcController, ]; const commandHandlers: Provider[] = [ CreateAuthenticationService, DeleteAuthenticationService, AddUsernameService, + DeleteUsernameService, ]; const mappers: Provider[] = [AuthenticationMapper, UsernameMapper]; diff --git a/src/modules/authentication/core/application/commands/delete-username/delete-username.command.ts b/src/modules/authentication/core/application/commands/delete-username/delete-username.command.ts new file mode 100644 index 0000000..241853f --- /dev/null +++ b/src/modules/authentication/core/application/commands/delete-username/delete-username.command.ts @@ -0,0 +1,10 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; + +export class DeleteUsernameCommand extends Command { + readonly name: string; + + constructor(props: CommandProps) { + super(props); + this.name = props.name; + } +} diff --git a/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts b/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts new file mode 100644 index 0000000..5812739 --- /dev/null +++ b/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts @@ -0,0 +1,23 @@ +import { Inject } from '@nestjs/common'; +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { DeleteUsernameCommand } from './delete-username.command'; +import { USERNAME_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { UsernameRepositoryPort } from '../../ports/username.repository.port'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; + +@CommandHandler(DeleteUsernameCommand) +export class DeleteUsernameService implements ICommandHandler { + constructor( + @Inject(USERNAME_REPOSITORY) + private readonly usernameRepository: UsernameRepositoryPort, + ) {} + + async execute(command: DeleteUsernameCommand): Promise { + const username: UsernameEntity = await this.usernameRepository.findOneById( + command.name, + ); + username.delete(); + const isDeleted: boolean = await this.usernameRepository.delete(username); + return isDeleted; + } +} diff --git a/src/modules/authentication/core/domain/events/username-deleted.domain-event.ts b/src/modules/authentication/core/domain/events/username-deleted.domain-event.ts new file mode 100644 index 0000000..2dd539f --- /dev/null +++ b/src/modules/authentication/core/domain/events/username-deleted.domain-event.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class UsernameDeletedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/authentication/core/domain/username.entity.ts b/src/modules/authentication/core/domain/username.entity.ts index d00806e..30d888b 100644 --- a/src/modules/authentication/core/domain/username.entity.ts +++ b/src/modules/authentication/core/domain/username.entity.ts @@ -1,6 +1,7 @@ import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library'; import { CreateUsernameProps, UsernameProps } from './username.types'; import { UsernameAddedDomainEvent } from './events/username-added.domain-event'; +import { UsernameDeletedDomainEvent } from './events/username-deleted.domain-event'; export class UsernameEntity extends AggregateRoot { protected readonly _id: AggregateID; @@ -23,6 +24,14 @@ export class UsernameEntity extends AggregateRoot { return username; }; + delete(): void { + this.addEvent( + new UsernameDeletedDomainEvent({ + aggregateId: this.id, + }), + ); + } + validate(): void { // entity business rules validation to protect it's invariant before saving entity to a database } diff --git a/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts new file mode 100644 index 0000000..389c78a --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts @@ -0,0 +1,45 @@ +import { + DatabaseErrorException, + NotFoundException, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { DeleteUsernameRequestDto } from './dtos/delete-username.request.dto'; +import { DeleteUsernameCommand } from '@modules/authentication/core/application/commands/delete-username/delete-username.command'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class DeleteUsernameGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'DeleteUsername') + async delete(data: DeleteUsernameRequestDto): Promise { + try { + await this.commandBus.execute(new DeleteUsernameCommand(data)); + } catch (error: any) { + if (error instanceof NotFoundException) + throw new RpcException({ + code: RpcExceptionCode.NOT_FOUND, + message: error.message, + }); + + if (error instanceof DatabaseErrorException) + throw new RpcException({ + code: RpcExceptionCode.INTERNAL, + message: error.message, + }); + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto.ts new file mode 100644 index 0000000..bd3d95d --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class DeleteUsernameRequestDto { + @IsString() + @IsNotEmpty() + name: string; +} diff --git a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts index 5f3de63..9e34a2f 100644 --- a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts @@ -50,6 +50,8 @@ describe('Authentication entity create', () => { expect(authenticationEntity.getProps().usernames.length).toBe(2); expect(authenticationEntity.domainEvents.length).toBe(1); }); +}); +describe('Authentication entity delete', () => { it('should delete an authentication entity', async () => { const authenticationEntity: AuthenticationEntity = await AuthenticationEntity.create(createAuthenticationProps); diff --git a/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts b/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts new file mode 100644 index 0000000..57d22e7 --- /dev/null +++ b/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts @@ -0,0 +1,54 @@ +import { USERNAME_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { DeleteUsernameCommand } from '@modules/authentication/core/application/commands/delete-username/delete-username.command'; +import { DeleteUsernameService } from '@modules/authentication/core/application/commands/delete-username/delete-username.service'; +import { DeleteUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto'; +import { Test, TestingModule } from '@nestjs/testing'; + +const deleteUsernameRequest: DeleteUsernameRequestDto = { + name: 'john.doe@email.com', +}; + +const mockUsernameEntity = { + delete: jest.fn(), +}; + +const mockUsernameRepository = { + findOneById: jest.fn().mockImplementation(() => mockUsernameEntity), + delete: jest.fn().mockImplementationOnce(() => true), +}; + +describe('Delete Username Service', () => { + let deleteUsernameService: DeleteUsernameService; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: USERNAME_REPOSITORY, + useValue: mockUsernameRepository, + }, + DeleteUsernameService, + ], + }).compile(); + + deleteUsernameService = module.get( + DeleteUsernameService, + ); + }); + + it('should be defined', () => { + expect(deleteUsernameService).toBeDefined(); + }); + + describe('execution', () => { + const deleteUsernameCommand = new DeleteUsernameCommand( + deleteUsernameRequest, + ); + it('should delete a username', async () => { + const result: boolean = await deleteUsernameService.execute( + deleteUsernameCommand, + ); + expect(result).toBeTruthy(); + }); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/username.entity.spec.ts b/src/modules/authentication/tests/unit/core/username.entity.spec.ts index 2cb19c5..65b2560 100644 --- a/src/modules/authentication/tests/unit/core/username.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/username.entity.spec.ts @@ -1,3 +1,4 @@ +import { UsernameDeletedDomainEvent } from '@modules/authentication/core/domain/events/username-deleted.domain-event'; import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; import { CreateUsernameProps, @@ -19,3 +20,15 @@ describe('Username entity create', () => { expect(usernameEntity.domainEvents.length).toBe(1); }); }); +describe('Username entity delete', () => { + it('should delete a username entity', async () => { + const usernameEntity: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + usernameEntity.delete(); + expect(usernameEntity.domainEvents.length).toBe(2); + expect(usernameEntity.domainEvents[1]).toBeInstanceOf( + UsernameDeletedDomainEvent, + ); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts index 8f070aa..bd5ea9d 100644 --- a/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts +++ b/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts @@ -56,7 +56,7 @@ describe('Delete Authentication Grpc Controller', () => { expect(deleteAuthenticationGrpcController).toBeDefined(); }); - it('should create a new authentication', async () => { + it('should delete an authentication', async () => { jest.spyOn(mockCommandBus, 'execute'); await deleteAuthenticationGrpcController.delete( deleteAuthenticationRequest, diff --git a/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts new file mode 100644 index 0000000..c4e795c --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts @@ -0,0 +1,99 @@ +import { + DatabaseErrorException, + NotFoundException, +} from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { DeleteUsernameGrpcController } from '@modules/authentication/interface/grpc-controllers/delete-username.grpc.controller'; +import { DeleteUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const deleteUsernameRequest: DeleteUsernameRequestDto = { + name: 'john.doe@email.com', +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new NotFoundException(); + }) + .mockImplementationOnce(() => { + throw new DatabaseErrorException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Delete Username Grpc Controller', () => { + let deleteUsernameGrpcController: DeleteUsernameGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + DeleteUsernameGrpcController, + ], + }).compile(); + + deleteUsernameGrpcController = module.get( + DeleteUsernameGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(deleteUsernameGrpcController).toBeDefined(); + }); + + it('should delete a username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await deleteUsernameGrpcController.delete(deleteUsernameRequest); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if username does not exist', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteUsernameGrpcController.delete(deleteUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.NOT_FOUND); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if a database error occurs', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteUsernameGrpcController.delete(deleteUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.INTERNAL); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteUsernameGrpcController.delete(deleteUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); From 805a7fe24d3554bf8319e4eeca606f590178b901 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Jul 2023 17:50:49 +0200 Subject: [PATCH 10/23] update username --- .../authentication/authentication.module.ts | 4 + .../update-username.command.ts | 13 +++ .../update-username.service.ts | 46 +++++++++ .../ports/username.repository.port.ts | 6 +- .../events/username-updated.domain-event.ts | 7 ++ .../core/domain/username.entity.ts | 16 ++- .../core/domain/username.types.ts | 4 + .../infrastructure/username.repository.ts | 18 ++++ .../dtos/update-username.request.dto.ts | 8 ++ .../update-username.grpc.controller.ts | 49 ++++++++++ .../unit/core/update-username.service.spec.ts | 97 +++++++++++++++++++ .../tests/unit/core/username.entity.spec.ts | 16 +++ .../update-username.grpc.controller.spec.ts | 88 +++++++++++++++++ src/modules/authentication/username.mapper.ts | 3 +- 14 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 src/modules/authentication/core/application/commands/update-username/update-username.command.ts create mode 100644 src/modules/authentication/core/application/commands/update-username/update-username.service.ts create mode 100644 src/modules/authentication/core/domain/events/username-updated.domain-event.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/update-username.request.dto.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/update-username.grpc.controller.ts create mode 100644 src/modules/authentication/tests/unit/core/update-username.service.spec.ts create mode 100644 src/modules/authentication/tests/unit/interface/update-username.grpc.controller.spec.ts diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 5084013..9b928ab 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -19,11 +19,14 @@ import { AddUsernameGrpcController } from './interface/grpc-controllers/add-user import { AddUsernameService } from './core/application/commands/add-username/add-username.service'; import { DeleteUsernameGrpcController } from './interface/grpc-controllers/delete-username.grpc.controller'; import { DeleteUsernameService } from './core/application/commands/delete-username/delete-username.service'; +import { UpdateUsernameGrpcController } from './interface/grpc-controllers/update-username.grpc.controller'; +import { UpdateUsernameService } from './core/application/commands/update-username/update-username.service'; const grpcControllers = [ CreateAuthenticationGrpcController, DeleteAuthenticationGrpcController, AddUsernameGrpcController, + UpdateUsernameGrpcController, DeleteUsernameGrpcController, ]; @@ -31,6 +34,7 @@ const commandHandlers: Provider[] = [ CreateAuthenticationService, DeleteAuthenticationService, AddUsernameService, + UpdateUsernameService, DeleteUsernameService, ]; diff --git a/src/modules/authentication/core/application/commands/update-username/update-username.command.ts b/src/modules/authentication/core/application/commands/update-username/update-username.command.ts new file mode 100644 index 0000000..ff9b67d --- /dev/null +++ b/src/modules/authentication/core/application/commands/update-username/update-username.command.ts @@ -0,0 +1,13 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; +import { Username } from '../../types/username'; + +export class UpdateUsernameCommand extends Command { + readonly userId: string; + readonly username: Username; + + constructor(props: CommandProps) { + super(props); + this.userId = props.userId; + this.username = props.username; + } +} diff --git a/src/modules/authentication/core/application/commands/update-username/update-username.service.ts b/src/modules/authentication/core/application/commands/update-username/update-username.service.ts new file mode 100644 index 0000000..b9ece39 --- /dev/null +++ b/src/modules/authentication/core/application/commands/update-username/update-username.service.ts @@ -0,0 +1,46 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { Inject } from '@nestjs/common'; +import { + AggregateID, + ConflictException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { USERNAME_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { UpdateUsernameCommand } from './update-username.command'; +import { UsernameRepositoryPort } from '../../ports/username.repository.port'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; + +@CommandHandler(UpdateUsernameCommand) +export class UpdateUsernameService implements ICommandHandler { + constructor( + @Inject(USERNAME_REPOSITORY) + private readonly usernameRepository: UsernameRepositoryPort, + ) {} + + async execute(command: UpdateUsernameCommand): Promise { + try { + const username: UsernameEntity = await this.usernameRepository.findByType( + command.userId, + command.username.type, + ); + const oldName: string = username.id; + username.update({ + name: command.username.name, + }); + await this.usernameRepository.updateUsername(oldName, username); + return username.getProps().name; + } catch (error: any) { + if (error instanceof ConflictException) { + throw new UsernameAlreadyExistsException(error); + } + if ( + error instanceof UniqueConstraintException && + error.message.includes('username') + ) { + throw new UsernameAlreadyExistsException(error); + } + throw error; + } + } +} diff --git a/src/modules/authentication/core/application/ports/username.repository.port.ts b/src/modules/authentication/core/application/ports/username.repository.port.ts index 1e11ac4..14ce69b 100644 --- a/src/modules/authentication/core/application/ports/username.repository.port.ts +++ b/src/modules/authentication/core/application/ports/username.repository.port.ts @@ -1,4 +1,8 @@ import { RepositoryPort } from '@mobicoop/ddd-library'; import { UsernameEntity } from '../../domain/username.entity'; +import { Type } from '../../domain/username.types'; -export type UsernameRepositoryPort = RepositoryPort; +export type UsernameRepositoryPort = RepositoryPort & { + findByType(userId: string, type: Type): Promise; + updateUsername(oldName: string, entity: UsernameEntity): Promise; +}; diff --git a/src/modules/authentication/core/domain/events/username-updated.domain-event.ts b/src/modules/authentication/core/domain/events/username-updated.domain-event.ts new file mode 100644 index 0000000..1a994bf --- /dev/null +++ b/src/modules/authentication/core/domain/events/username-updated.domain-event.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class UsernameUpdatedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/authentication/core/domain/username.entity.ts b/src/modules/authentication/core/domain/username.entity.ts index 30d888b..b9e9e92 100644 --- a/src/modules/authentication/core/domain/username.entity.ts +++ b/src/modules/authentication/core/domain/username.entity.ts @@ -1,7 +1,12 @@ import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library'; -import { CreateUsernameProps, UsernameProps } from './username.types'; +import { + CreateUsernameProps, + UpdateUsernameProps, + UsernameProps, +} from './username.types'; import { UsernameAddedDomainEvent } from './events/username-added.domain-event'; import { UsernameDeletedDomainEvent } from './events/username-deleted.domain-event'; +import { UsernameUpdatedDomainEvent } from './events/username-updated.domain-event'; export class UsernameEntity extends AggregateRoot { protected readonly _id: AggregateID; @@ -24,6 +29,15 @@ export class UsernameEntity extends AggregateRoot { return username; }; + update(props: UpdateUsernameProps): void { + this.props.name = props.name; + this.addEvent( + new UsernameUpdatedDomainEvent({ + aggregateId: props.name, + }), + ); + } + delete(): void { this.addEvent( new UsernameDeletedDomainEvent({ diff --git a/src/modules/authentication/core/domain/username.types.ts b/src/modules/authentication/core/domain/username.types.ts index 6f671b0..4b91d4b 100644 --- a/src/modules/authentication/core/domain/username.types.ts +++ b/src/modules/authentication/core/domain/username.types.ts @@ -10,6 +10,10 @@ export interface CreateUsernameProps { type: Type; } +export interface UpdateUsernameProps { + name: string; +} + export enum Type { EMAIL = 'EMAIL', PHONE = 'PHONE', diff --git a/src/modules/authentication/infrastructure/username.repository.ts b/src/modules/authentication/infrastructure/username.repository.ts index 1ee3e68..22e5148 100644 --- a/src/modules/authentication/infrastructure/username.repository.ts +++ b/src/modules/authentication/infrastructure/username.repository.ts @@ -10,6 +10,7 @@ import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; import { UsernameEntity } from '../core/domain/username.entity'; import { UsernameRepositoryPort } from '../core/application/ports/username.repository.port'; import { UsernameMapper } from '../username.mapper'; +import { Type } from '../core/domain/username.types'; export type UsernameModel = { username: string; @@ -46,4 +47,21 @@ export class UsernameRepository }), ); } + + findByType = async (userId: string, type: Type): Promise => + this.findOne({ + authUuid: userId, + type, + }); + + updateUsername = async ( + oldName: string, + entity: UsernameEntity, + ): Promise => + this.updateWhere( + { + username: oldName, + }, + entity, + ); } diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/update-username.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/update-username.request.dto.ts new file mode 100644 index 0000000..fb063ac --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/update-username.request.dto.ts @@ -0,0 +1,8 @@ +import { IsNotEmpty, IsString } from 'class-validator'; +import { UsernameDto } from './username.dto'; + +export class UpdateUsernameRequestDto extends UsernameDto { + @IsString() + @IsNotEmpty() + userId: string; +} diff --git a/src/modules/authentication/interface/grpc-controllers/update-username.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/update-username.grpc.controller.ts new file mode 100644 index 0000000..284331f --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/update-username.grpc.controller.ts @@ -0,0 +1,49 @@ +import { + AggregateID, + IdResponse, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { UpdateUsernameRequestDto } from './dtos/update-username.request.dto'; +import { UpdateUsernameCommand } from '@modules/authentication/core/application/commands/update-username/update-username.command'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class UpdateUsernameGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'UpdateUsername') + async updateUsername(data: UpdateUsernameRequestDto): Promise { + try { + const aggregateID: AggregateID = await this.commandBus.execute( + new UpdateUsernameCommand({ + userId: data.userId, + username: { + name: data.name, + type: data.type, + }, + }), + ); + return new IdResponse(aggregateID); + } catch (error: any) { + if (error instanceof UsernameAlreadyExistsException) + throw new RpcException({ + code: RpcExceptionCode.ALREADY_EXISTS, + message: error.message, + }); + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/authentication/tests/unit/core/update-username.service.spec.ts b/src/modules/authentication/tests/unit/core/update-username.service.spec.ts new file mode 100644 index 0000000..7e0ba2b --- /dev/null +++ b/src/modules/authentication/tests/unit/core/update-username.service.spec.ts @@ -0,0 +1,97 @@ +import { + AggregateID, + ConflictException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { USERNAME_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { UpdateUsernameCommand } from '@modules/authentication/core/application/commands/update-username/update-username.command'; +import { UpdateUsernameService } from '@modules/authentication/core/application/commands/update-username/update-username.service'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { UpdateUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/update-username.request.dto'; +import { Test, TestingModule } from '@nestjs/testing'; + +const updateUsernameRequest: UpdateUsernameRequestDto = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + name: 'new-john.doe@email.com', + type: Type.EMAIL, +}; + +const mockUsernameRepository = { + findByType: jest.fn().mockImplementation(() => ({ + id: 'john.doe@email.com', + update: jest.fn(), + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + name: 'new-john.doe@email.com', + type: Type.EMAIL, + })), + })), + updateUsername: jest + .fn() + .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new ConflictException('already exists'); + }) + .mockImplementationOnce(() => { + throw new UniqueConstraintException('username'); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Update Username Service', () => { + let updateUsernameService: UpdateUsernameService; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: USERNAME_REPOSITORY, + useValue: mockUsernameRepository, + }, + UpdateUsernameService, + ], + }).compile(); + + updateUsernameService = module.get( + UpdateUsernameService, + ); + }); + + it('should be defined', () => { + expect(updateUsernameService).toBeDefined(); + }); + + describe('execution', () => { + const updateUsernameCommand = new UpdateUsernameCommand({ + userId: updateUsernameRequest.userId, + username: { + name: updateUsernameRequest.name, + type: updateUsernameRequest.type, + }, + }); + it('should update a username', async () => { + const result: AggregateID = await updateUsernameService.execute( + updateUsernameCommand, + ); + expect(result).toBe('new-john.doe@email.com'); + }); + it('should throw a dedicated exception if username already exists for this type', async () => { + await expect( + updateUsernameService.execute(updateUsernameCommand), + ).rejects.toBeInstanceOf(UsernameAlreadyExistsException); + }); + it('should throw a dedicated exception if username already exists for this name', async () => { + await expect( + updateUsernameService.execute(updateUsernameCommand), + ).rejects.toBeInstanceOf(UsernameAlreadyExistsException); + }); + it('should throw an error if something bad happens', async () => { + await expect( + updateUsernameService.execute(updateUsernameCommand), + ).rejects.toBeInstanceOf(Error); + }); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/username.entity.spec.ts b/src/modules/authentication/tests/unit/core/username.entity.spec.ts index 65b2560..d7cb795 100644 --- a/src/modules/authentication/tests/unit/core/username.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/username.entity.spec.ts @@ -1,4 +1,5 @@ import { UsernameDeletedDomainEvent } from '@modules/authentication/core/domain/events/username-deleted.domain-event'; +import { UsernameUpdatedDomainEvent } from '@modules/authentication/core/domain/events/username-updated.domain-event'; import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; import { CreateUsernameProps, @@ -20,6 +21,21 @@ describe('Username entity create', () => { expect(usernameEntity.domainEvents.length).toBe(1); }); }); +describe('Username entity update', () => { + it('should update a username entity', async () => { + const usernameEntity: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + usernameEntity.update({ + name: 'new-john.doe@email.com', + }); + expect(usernameEntity.getProps().name).toBe('new-john.doe@email.com'); + expect(usernameEntity.domainEvents.length).toBe(2); + expect(usernameEntity.domainEvents[1]).toBeInstanceOf( + UsernameUpdatedDomainEvent, + ); + }); +}); describe('Username entity delete', () => { it('should delete a username entity', async () => { const usernameEntity: UsernameEntity = await UsernameEntity.create( diff --git a/src/modules/authentication/tests/unit/interface/update-username.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/update-username.grpc.controller.spec.ts new file mode 100644 index 0000000..c9a7d32 --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/update-username.grpc.controller.spec.ts @@ -0,0 +1,88 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { UpdateUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/update-username.request.dto'; +import { UpdateUsernameGrpcController } from '@modules/authentication/interface/grpc-controllers/update-username.grpc.controller'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const updateUsernameRequest: UpdateUsernameRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', + name: 'new-john.doe@email.com', + type: Type.EMAIL, +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => 'new-john.doe@email.com') + .mockImplementationOnce(() => { + throw new UsernameAlreadyExistsException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Update Username Grpc Controller', () => { + let updateUsernameGrpcController: UpdateUsernameGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + UpdateUsernameGrpcController, + ], + }).compile(); + + updateUsernameGrpcController = module.get( + UpdateUsernameGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(updateUsernameGrpcController).toBeDefined(); + }); + + it('should update a username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + const result: IdResponse = + await updateUsernameGrpcController.updateUsername(updateUsernameRequest); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('new-john.doe@email.com'); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if username already exists', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await updateUsernameGrpcController.updateUsername(updateUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await updateUsernameGrpcController.updateUsername(updateUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/username.mapper.ts b/src/modules/authentication/username.mapper.ts index b31a906..33d399b 100644 --- a/src/modules/authentication/username.mapper.ts +++ b/src/modules/authentication/username.mapper.ts @@ -31,12 +31,13 @@ export class UsernameMapper toDomain = (record: UsernameModel): UsernameEntity => { const entity = new UsernameEntity({ - id: record.authUuid, + id: record.username, createdAt: new Date(record.createdAt), updatedAt: new Date(record.updatedAt), props: { name: record.username, type: Type[record.type], + userId: record.authUuid, }, }); return entity; From 55c43677023d18d5453ac723db055cd6d56b4b42 Mon Sep 17 00:00:00 2001 From: sbriat Date: Mon, 10 Jul 2023 12:31:36 +0200 Subject: [PATCH 11/23] repository tests --- .../username.repository.spec.ts | 81 ++++++++++++++++--- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts index 16f9124..23842f9 100644 --- a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts +++ b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts @@ -2,13 +2,37 @@ import { UsernameMapper } from '@modules/authentication/username.mapper'; import { PrismaService } from '@modules/authentication/infrastructure/prisma.service'; import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; -import { UsernameRepository } from '@modules/authentication/infrastructure/username.repository'; +import { + UsernameModel, + UsernameRepository, +} from '@modules/authentication/infrastructure/username.repository'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; + +const mockPrismaService = { + username: { + findFirst: jest.fn().mockImplementation(async () => { + const now = new Date('2023-06-21 06:00:00'); + const record: UsernameModel = { + authUuid: '330bd6de-1eb8-450b-8674-0e3c9209f048', + type: Type.EMAIL, + username: 'john.doe@email.com', + createdAt: now, + updatedAt: now, + }; + return record; + }), + + update: jest.fn().mockImplementation(), + }, +}; const mockMessagePublisher = { publish: jest.fn().mockImplementation(), }; describe('Username repository', () => { + let usernameRepository: UsernameRepository; let prismaService: PrismaService; let usernameMapper: UsernameMapper; let eventEmitter: EventEmitter2; @@ -16,21 +40,58 @@ describe('Username repository', () => { beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [EventEmitterModule.forRoot()], - providers: [PrismaService, UsernameMapper], + providers: [ + { + provide: PrismaService, + useValue: mockPrismaService, + }, + UsernameMapper, + ], }).compile(); prismaService = module.get(PrismaService); usernameMapper = module.get(UsernameMapper); eventEmitter = module.get(EventEmitter2); + usernameRepository = new UsernameRepository( + prismaService, + usernameMapper, + eventEmitter, + mockMessagePublisher, + ); }); it('should be defined', () => { - expect( - new UsernameRepository( - prismaService, - usernameMapper, - eventEmitter, - mockMessagePublisher, - ), - ).toBeDefined(); + expect(usernameRepository).toBeDefined(); + }); + it('should find a username by its userId and Type', async () => { + jest.spyOn(usernameRepository, 'findOne'); + const username: UsernameEntity = await usernameRepository.findByType( + '330bd6de-1eb8-450b-8674-0e3c9209f048', + Type.EMAIL, + ); + expect(usernameRepository.findOne).toHaveBeenCalledTimes(1); + expect(usernameRepository.findOne).toHaveBeenCalledWith({ + authUuid: '330bd6de-1eb8-450b-8674-0e3c9209f048', + type: Type.EMAIL, + }); + expect(username.getProps().name).toBe('john.doe@email.com'); + }); + it('should update a username', async () => { + jest.spyOn(usernameRepository, 'updateWhere'); + const usernameToUpdate: UsernameEntity = await UsernameEntity.create({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + type: Type.EMAIL, + name: 'john.doe@new-email.com', + }); + await usernameRepository.updateUsername( + 'john.doe@email.com', + usernameToUpdate, + ); + expect(usernameRepository.updateWhere).toHaveBeenCalledTimes(1); + expect(usernameRepository.updateWhere).toHaveBeenCalledWith( + { + username: 'john.doe@email.com', + }, + usernameToUpdate, + ); }); }); From de8132575094d16a3002ee5e6d60d334f42da293 Mon Sep 17 00:00:00 2001 From: sbriat Date: Mon, 10 Jul 2023 17:37:50 +0200 Subject: [PATCH 12/23] wip tests update password --- .../authentication/authentication.mapper.ts | 16 ++--- .../authentication/authentication.module.ts | 4 ++ .../update-password.command.ts | 12 ++++ .../update-password.service.ts | 23 +++++++ .../core/domain/authentication.entity.ts | 15 ++++- .../events/password-updated.domain-event.ts | 7 +++ .../dtos/update-password.request.dto.ts | 11 ++++ .../update-password.grpc.controller.ts | 40 ++++++++++++ .../unit/core/update-password.service.spec.ts | 62 +++++++++++++++++++ 9 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 src/modules/authentication/core/application/commands/update-password/update-password.command.ts create mode 100644 src/modules/authentication/core/application/commands/update-password/update-password.service.ts create mode 100644 src/modules/authentication/core/domain/events/password-updated.domain-event.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/update-password.grpc.controller.ts create mode 100644 src/modules/authentication/tests/unit/core/update-password.service.spec.ts diff --git a/src/modules/authentication/authentication.mapper.ts b/src/modules/authentication/authentication.mapper.ts index 367fd07..7566f68 100644 --- a/src/modules/authentication/authentication.mapper.ts +++ b/src/modules/authentication/authentication.mapper.ts @@ -31,12 +31,14 @@ export class AuthenticationMapper const record: AuthenticationWriteModel = { uuid: copy.id, password: copy.password, - usernames: { - create: copy.usernames.map((username: UsernameProps) => ({ - username: username.name, - type: username.type, - })), - }, + usernames: copy.usernames + ? { + create: copy.usernames.map((username: UsernameProps) => ({ + username: username.name, + type: username.type, + })), + } + : undefined, createdAt: copy.createdAt, updatedAt: copy.updatedAt, }; @@ -51,7 +53,7 @@ export class AuthenticationMapper props: { userId: record.uuid, password: record.password, - usernames: record.usernames.map((username: UsernameModel) => ({ + usernames: record.usernames?.map((username: UsernameModel) => ({ userId: record.uuid, name: username.username, type: Type[username.type], diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 9b928ab..c319fa3 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -21,6 +21,8 @@ import { DeleteUsernameGrpcController } from './interface/grpc-controllers/delet import { DeleteUsernameService } from './core/application/commands/delete-username/delete-username.service'; import { UpdateUsernameGrpcController } from './interface/grpc-controllers/update-username.grpc.controller'; import { UpdateUsernameService } from './core/application/commands/update-username/update-username.service'; +import { UpdatePasswordGrpcController } from './interface/grpc-controllers/update-password.grpc.controller'; +import { UpdatePasswordService } from './core/application/commands/update-password/update-password.service'; const grpcControllers = [ CreateAuthenticationGrpcController, @@ -28,6 +30,7 @@ const grpcControllers = [ AddUsernameGrpcController, UpdateUsernameGrpcController, DeleteUsernameGrpcController, + UpdatePasswordGrpcController, ]; const commandHandlers: Provider[] = [ @@ -36,6 +39,7 @@ const commandHandlers: Provider[] = [ AddUsernameService, UpdateUsernameService, DeleteUsernameService, + UpdatePasswordService, ]; const mappers: Provider[] = [AuthenticationMapper, UsernameMapper]; diff --git a/src/modules/authentication/core/application/commands/update-password/update-password.command.ts b/src/modules/authentication/core/application/commands/update-password/update-password.command.ts new file mode 100644 index 0000000..5e11f2c --- /dev/null +++ b/src/modules/authentication/core/application/commands/update-password/update-password.command.ts @@ -0,0 +1,12 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; + +export class UpdatePasswordCommand extends Command { + readonly userId: string; + readonly password: string; + + constructor(props: CommandProps) { + super(props); + this.userId = props.userId; + this.password = props.password; + } +} diff --git a/src/modules/authentication/core/application/commands/update-password/update-password.service.ts b/src/modules/authentication/core/application/commands/update-password/update-password.service.ts new file mode 100644 index 0000000..61ef660 --- /dev/null +++ b/src/modules/authentication/core/application/commands/update-password/update-password.service.ts @@ -0,0 +1,23 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { Inject } from '@nestjs/common'; +import { AggregateID } from '@mobicoop/ddd-library'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { UpdatePasswordCommand } from './update-password.command'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; + +@CommandHandler(UpdatePasswordCommand) +export class UpdatePasswordService implements ICommandHandler { + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly authenticationRepository: AuthenticationRepositoryPort, + ) {} + + async execute(command: UpdatePasswordCommand): Promise { + const authentication: AuthenticationEntity = + await this.authenticationRepository.findOneById(command.userId); + await authentication.updatePassword(command.password); + await this.authenticationRepository.update(command.userId, authentication); + return authentication.id; + } +} diff --git a/src/modules/authentication/core/domain/authentication.entity.ts b/src/modules/authentication/core/domain/authentication.entity.ts index 81ed651..6b900af 100644 --- a/src/modules/authentication/core/domain/authentication.entity.ts +++ b/src/modules/authentication/core/domain/authentication.entity.ts @@ -6,6 +6,7 @@ import { } from './authentication.types'; import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-event'; import { AuthenticationDeletedDomainEvent } from './events/authentication-deleted.domain-event'; +import { PasswordUpdatedDomainEvent } from './events/password-updated.domain-event'; export class AuthenticationEntity extends AggregateRoot { protected readonly _id: AggregateID; @@ -14,7 +15,7 @@ export class AuthenticationEntity extends AggregateRoot { create: CreateAuthenticationProps, ): Promise => { const props: AuthenticationProps = { ...create }; - const hash = await bcrypt.hash(props.password, 10); + const hash = await AuthenticationEntity.encryptPassword(props.password); const authentication = new AuthenticationEntity({ id: props.userId, props: { @@ -29,6 +30,15 @@ export class AuthenticationEntity extends AggregateRoot { return authentication; }; + updatePassword = async (password: string): Promise => { + this.props.password = await AuthenticationEntity.encryptPassword(password); + this.addEvent( + new PasswordUpdatedDomainEvent({ + aggregateId: this.id, + }), + ); + }; + delete(): void { this.addEvent( new AuthenticationDeletedDomainEvent({ @@ -40,4 +50,7 @@ export class AuthenticationEntity extends AggregateRoot { validate(): void { // entity business rules validation to protect it's invariant before saving entity to a database } + + private static encryptPassword = async (password: string): Promise => + await bcrypt.hash(password, 10); } diff --git a/src/modules/authentication/core/domain/events/password-updated.domain-event.ts b/src/modules/authentication/core/domain/events/password-updated.domain-event.ts new file mode 100644 index 0000000..f644476 --- /dev/null +++ b/src/modules/authentication/core/domain/events/password-updated.domain-event.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class PasswordUpdatedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto.ts new file mode 100644 index 0000000..ba76c1f --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class UpdatePasswordRequestDto { + @IsString() + @IsNotEmpty() + userId: string; + + @IsString() + @IsNotEmpty() + password: string; +} diff --git a/src/modules/authentication/interface/grpc-controllers/update-password.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/update-password.grpc.controller.ts new file mode 100644 index 0000000..9453f8b --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/update-password.grpc.controller.ts @@ -0,0 +1,40 @@ +import { + AggregateID, + IdResponse, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { UpdatePasswordRequestDto } from './dtos/update-password.request.dto'; +import { UpdatePasswordCommand } from '@modules/authentication/core/application/commands/update-password/update-password.command'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class UpdatePasswordGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'UpdatePassword') + async updatePassword(data: UpdatePasswordRequestDto): Promise { + try { + const aggregateID: AggregateID = await this.commandBus.execute( + new UpdatePasswordCommand({ + userId: data.userId, + password: data.password, + }), + ); + return new IdResponse(aggregateID); + } catch (error: any) { + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/authentication/tests/unit/core/update-password.service.spec.ts b/src/modules/authentication/tests/unit/core/update-password.service.spec.ts new file mode 100644 index 0000000..57ede71 --- /dev/null +++ b/src/modules/authentication/tests/unit/core/update-password.service.spec.ts @@ -0,0 +1,62 @@ +import { AggregateID } from '@mobicoop/ddd-library'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { UpdatePasswordService } from '@modules/authentication/core/application/commands/update-password/update-password.service'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { UpdatePasswordRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto'; +import { Test, TestingModule } from '@nestjs/testing'; +import { UpdatePasswordCommand } from '@modules/authentication/core/application/commands/update-password/update-password.command'; + +const updatePasswordRequest: UpdatePasswordRequestDto = { + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + password: '@Br@ndN3wPa$$w0rd', +}; + +const mockAuthenticationRepository = { + findOneById: jest.fn().mockImplementation(() => ({ + id: '165192d4-398a-4469-a16b-98c02cc6f531', + updatePassword: jest.fn(), + getProps: jest.fn().mockImplementation(() => ({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + name: 'john.doe@email.com', + type: Type.EMAIL, + })), + })), + update: jest.fn().mockImplementation(), +}; + +describe('Update Password Service', () => { + let updatePasswordService: UpdatePasswordService; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: AUTHENTICATION_REPOSITORY, + useValue: mockAuthenticationRepository, + }, + UpdatePasswordService, + ], + }).compile(); + + updatePasswordService = module.get( + UpdatePasswordService, + ); + }); + + it('should be defined', () => { + expect(updatePasswordService).toBeDefined(); + }); + + describe('execution', () => { + const updatePasswordCommand = new UpdatePasswordCommand({ + userId: updatePasswordRequest.userId, + password: updatePasswordRequest.password, + }); + it('should update the password', async () => { + const result: AggregateID = await updatePasswordService.execute( + updatePasswordCommand, + ); + expect(result).toBe('165192d4-398a-4469-a16b-98c02cc6f531'); + }); + }); +}); From 92ce0cd93a7ec654bd89d1e7543cce65e3c7c8ce Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 11 Jul 2023 09:04:40 +0200 Subject: [PATCH 13/23] update password --- .../unit/core/authentication.entity.spec.ts | 14 ++++ .../unit/core/update-password.service.spec.ts | 2 +- .../update-password.grpc.controller.spec.ts | 70 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/modules/authentication/tests/unit/interface/update-password.grpc.controller.spec.ts diff --git a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts index 9e34a2f..1d5f7dd 100644 --- a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts @@ -1,6 +1,7 @@ import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; import { CreateAuthenticationProps } from '@modules/authentication/core/domain/authentication.types'; import { AuthenticationDeletedDomainEvent } from '@modules/authentication/core/domain/events/authentication-deleted.domain-event'; +import { PasswordUpdatedDomainEvent } from '@modules/authentication/core/domain/events/password-updated.domain-event'; import { Type } from '@modules/authentication/core/domain/username.types'; const createAuthenticationProps: CreateAuthenticationProps = { @@ -62,3 +63,16 @@ describe('Authentication entity delete', () => { ); }); }); +describe('Authentication password update', () => { + it('should update the password of an authentication entity', async () => { + const authenticationEntity: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + const oldPassword: string = authenticationEntity.getProps().password; + await authenticationEntity.updatePassword('@Br@ndN3wP@$$w0rd'); + expect(authenticationEntity.domainEvents.length).toBe(2); + expect(authenticationEntity.domainEvents[1]).toBeInstanceOf( + PasswordUpdatedDomainEvent, + ); + expect(authenticationEntity.getProps().password).not.toBe(oldPassword); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/update-password.service.spec.ts b/src/modules/authentication/tests/unit/core/update-password.service.spec.ts index 57ede71..3270f0f 100644 --- a/src/modules/authentication/tests/unit/core/update-password.service.spec.ts +++ b/src/modules/authentication/tests/unit/core/update-password.service.spec.ts @@ -8,7 +8,7 @@ import { UpdatePasswordCommand } from '@modules/authentication/core/application/ const updatePasswordRequest: UpdatePasswordRequestDto = { userId: '165192d4-398a-4469-a16b-98c02cc6f531', - password: '@Br@ndN3wPa$$w0rd', + password: '@Br@ndN3wP@$$w0rd', }; const mockAuthenticationRepository = { diff --git a/src/modules/authentication/tests/unit/interface/update-password.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/update-password.grpc.controller.spec.ts new file mode 100644 index 0000000..3618d37 --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/update-password.grpc.controller.spec.ts @@ -0,0 +1,70 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { UpdatePasswordRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto'; +import { UpdatePasswordGrpcController } from '@modules/authentication/interface/grpc-controllers/update-password.grpc.controller'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const updatePasswordRequest: UpdatePasswordRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', + password: '@Br@ndN3wP@$$w0rd', +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => '330bd6de-1eb8-450b-8674-0e3c9209f048') + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Update Password Grpc Controller', () => { + let updatePasswordGrpcController: UpdatePasswordGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + UpdatePasswordGrpcController, + ], + }).compile(); + + updatePasswordGrpcController = module.get( + UpdatePasswordGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(updatePasswordGrpcController).toBeDefined(); + }); + + it('should update the password', async () => { + jest.spyOn(mockCommandBus, 'execute'); + const result: IdResponse = + await updatePasswordGrpcController.updatePassword(updatePasswordRequest); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('330bd6de-1eb8-450b-8674-0e3c9209f048'); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await updatePasswordGrpcController.updatePassword(updatePasswordRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); From 487ae9c38e5c5471f311b4a74f0cff08c514947a Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 11 Jul 2023 11:43:56 +0200 Subject: [PATCH 14/23] validate authentication use case --- .../authentication/authentication.module.ts | 6 + .../ports/username.repository.port.ts | 1 + .../validate-authentication.query-handler.ts | 50 ++++++++ .../validate-authentication.query.ts | 12 ++ .../core/domain/authentication.entity.ts | 3 + .../infrastructure/username.repository.ts | 5 + .../grpc-controllers/authentication.proto | 6 +- .../validate-authentication.request.dto.ts | 11 ++ ...validate-authentication.grpc.controller.ts | 37 ++++++ .../unit/core/authentication.entity.spec.ts | 18 +++ ...idate-authentication.query-handler.spec.ts | 120 ++++++++++++++++++ .../username.repository.spec.ts | 16 +++ ...ate-authentication.grpc.controller.spec.ts | 75 +++++++++++ 13 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts create mode 100644 src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/dtos/validate-authentication.request.dto.ts create mode 100644 src/modules/authentication/interface/grpc-controllers/validate-authentication.grpc.controller.ts create mode 100644 src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts create mode 100644 src/modules/authentication/tests/unit/interface/validate-authentication.grpc.controller.spec.ts diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index c319fa3..539f55b 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -23,6 +23,8 @@ import { UpdateUsernameGrpcController } from './interface/grpc-controllers/updat import { UpdateUsernameService } from './core/application/commands/update-username/update-username.service'; import { UpdatePasswordGrpcController } from './interface/grpc-controllers/update-password.grpc.controller'; import { UpdatePasswordService } from './core/application/commands/update-password/update-password.service'; +import { ValidateAuthenticationGrpcController } from './interface/grpc-controllers/validate-authentication.grpc.controller'; +import { ValidateAuthenticationQueryHandler } from './core/application/queries/validate-authentication/validate-authentication.query-handler'; const grpcControllers = [ CreateAuthenticationGrpcController, @@ -31,6 +33,7 @@ const grpcControllers = [ UpdateUsernameGrpcController, DeleteUsernameGrpcController, UpdatePasswordGrpcController, + ValidateAuthenticationGrpcController, ]; const commandHandlers: Provider[] = [ @@ -42,6 +45,8 @@ const commandHandlers: Provider[] = [ UpdatePasswordService, ]; +const queryHandlers: Provider[] = [ValidateAuthenticationQueryHandler]; + const mappers: Provider[] = [AuthenticationMapper, UsernameMapper]; const repositories: Provider[] = [ @@ -69,6 +74,7 @@ const orms: Provider[] = [PrismaService]; controllers: [...grpcControllers], providers: [ ...commandHandlers, + ...queryHandlers, ...mappers, ...repositories, ...messageBrokers, diff --git a/src/modules/authentication/core/application/ports/username.repository.port.ts b/src/modules/authentication/core/application/ports/username.repository.port.ts index 14ce69b..9188472 100644 --- a/src/modules/authentication/core/application/ports/username.repository.port.ts +++ b/src/modules/authentication/core/application/ports/username.repository.port.ts @@ -4,5 +4,6 @@ import { Type } from '../../domain/username.types'; export type UsernameRepositoryPort = RepositoryPort & { findByType(userId: string, type: Type): Promise; + findByName(name: string): Promise; updateUsername(oldName: string, entity: UsernameEntity): Promise; }; diff --git a/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts b/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts new file mode 100644 index 0000000..88aa703 --- /dev/null +++ b/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts @@ -0,0 +1,50 @@ +import { IQueryHandler, QueryHandler } from '@nestjs/cqrs'; +import { Inject, UnauthorizedException } from '@nestjs/common'; +import { ValidateAuthenticationQuery } from './validate-authentication.query'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { UsernameRepositoryPort } from '../../ports/username.repository.port'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; +import { AggregateID, NotFoundException } from '@mobicoop/ddd-library'; + +@QueryHandler(ValidateAuthenticationQuery) +export class ValidateAuthenticationQueryHandler implements IQueryHandler { + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly authenticationRepository: AuthenticationRepositoryPort, + @Inject(USERNAME_REPOSITORY) + private readonly usernameRepository: UsernameRepositoryPort, + ) {} + + execute = async ( + query: ValidateAuthenticationQuery, + ): Promise => { + let usernameEntity: UsernameEntity; + try { + usernameEntity = await this.usernameRepository.findByName(query.name); + } catch (e) { + throw new NotFoundException(); + } + let authenticationEntity: AuthenticationEntity; + try { + authenticationEntity = await this.authenticationRepository.findOneById( + usernameEntity.getProps().userId, + ); + } catch (e) { + throw new NotFoundException(); + } + try { + const isAuthenticated = await authenticationEntity.authenticate( + query.password, + ); + if (isAuthenticated) return authenticationEntity.id; + throw new UnauthorizedException(); + } catch (e) { + throw new UnauthorizedException(); + } + }; +} diff --git a/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query.ts b/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query.ts new file mode 100644 index 0000000..f979a9e --- /dev/null +++ b/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query.ts @@ -0,0 +1,12 @@ +import { QueryBase } from '@mobicoop/ddd-library'; + +export class ValidateAuthenticationQuery extends QueryBase { + readonly name: string; + readonly password: string; + + constructor(name: string, password: string) { + super(); + this.name = name; + this.password = password; + } +} diff --git a/src/modules/authentication/core/domain/authentication.entity.ts b/src/modules/authentication/core/domain/authentication.entity.ts index 6b900af..184a0af 100644 --- a/src/modules/authentication/core/domain/authentication.entity.ts +++ b/src/modules/authentication/core/domain/authentication.entity.ts @@ -47,6 +47,9 @@ export class AuthenticationEntity extends AggregateRoot { ); } + authenticate = async (password: string): Promise => + await bcrypt.compare(password, this.props.password); + validate(): void { // entity business rules validation to protect it's invariant before saving entity to a database } diff --git a/src/modules/authentication/infrastructure/username.repository.ts b/src/modules/authentication/infrastructure/username.repository.ts index 22e5148..9342bdd 100644 --- a/src/modules/authentication/infrastructure/username.repository.ts +++ b/src/modules/authentication/infrastructure/username.repository.ts @@ -54,6 +54,11 @@ export class UsernameRepository type, }); + findByName = async (name: string): Promise => + this.findOne({ + username: name, + }); + updateUsername = async ( oldName: string, entity: UsernameEntity, diff --git a/src/modules/authentication/interface/grpc-controllers/authentication.proto b/src/modules/authentication/interface/grpc-controllers/authentication.proto index 87d04f7..604b09c 100644 --- a/src/modules/authentication/interface/grpc-controllers/authentication.proto +++ b/src/modules/authentication/interface/grpc-controllers/authentication.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package authentication; service AuthenticationService { - rpc Validate(AuthenticationByUsernamePassword) returns (Id); + rpc Validate(AuthenticationByNamePassword) returns (Id); rpc Create(Authentication) returns (Id); rpc AddUsername(Username) returns (Id); rpc UpdatePassword(Password) returns (Id); @@ -12,8 +12,8 @@ service AuthenticationService { rpc Delete(UserId) returns (Empty); } -message AuthenticationByUsernamePassword { - string username = 1; +message AuthenticationByNamePassword { + string name = 1; string password = 2; } diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/validate-authentication.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/validate-authentication.request.dto.ts new file mode 100644 index 0000000..6637cb8 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/validate-authentication.request.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class ValidateAuthenticationRequestDto { + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsNotEmpty() + password: string; +} diff --git a/src/modules/authentication/interface/grpc-controllers/validate-authentication.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/validate-authentication.grpc.controller.ts new file mode 100644 index 0000000..71f0673 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/validate-authentication.grpc.controller.ts @@ -0,0 +1,37 @@ +import { + AggregateID, + IdResponse, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { QueryBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { ValidateAuthenticationRequestDto } from './dtos/validate-authentication.request.dto'; +import { ValidateAuthenticationQuery } from '@modules/authentication/core/application/queries/validate-authentication/validate-authentication.query'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class ValidateAuthenticationGrpcController { + constructor(private readonly queryBus: QueryBus) {} + + @GrpcMethod('AuthenticationService', 'Validate') + async validate(data: ValidateAuthenticationRequestDto): Promise { + try { + const aggregateID: AggregateID = await this.queryBus.execute( + new ValidateAuthenticationQuery(data.name, data.password), + ); + return new IdResponse(aggregateID); + } catch (error: any) { + throw new RpcException({ + code: RpcExceptionCode.PERMISSION_DENIED, + message: 'Permission denied', + }); + } + } +} diff --git a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts index 1d5f7dd..761beb7 100644 --- a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts @@ -76,3 +76,21 @@ describe('Authentication password update', () => { expect(authenticationEntity.getProps().password).not.toBe(oldPassword); }); }); +describe('Authentication password validation', () => { + it('should validate a valid password', async () => { + const authenticationEntity: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + const result: boolean = await authenticationEntity.authenticate( + 'somePassword', + ); + expect(result).toBeTruthy(); + }); + it('should not validate an invalid password', async () => { + const authenticationEntity: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + const result: boolean = await authenticationEntity.authenticate( + 'someWrongPassword', + ); + expect(result).toBeFalsy(); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts b/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts new file mode 100644 index 0000000..dd71fe3 --- /dev/null +++ b/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts @@ -0,0 +1,120 @@ +import { AggregateID, NotFoundException } from '@mobicoop/ddd-library'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; +import { ValidateAuthenticationQuery } from '@modules/authentication/core/application/queries/validate-authentication/validate-authentication.query'; +import { ValidateAuthenticationQueryHandler } from '@modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler'; +import { UnauthorizedException } from '@nestjs/common'; + +import { Test, TestingModule } from '@nestjs/testing'; + +const validName = 'john.doe@email.com'; +const furtherInvalidName = 'validNameButInvalidAuthenticationForSomeReason'; +const validUserId = '165192d4-398a-4469-a16b-98c02cc6f531'; +const invalidUserId = 'b3b75798-7104-4151-adab-11fe16e32d85'; +const validPassword = 'V@l1dP@$$w0rd'; +const invalidPassword = '1nV@l1dP@$$w0rd'; + +const mockUsernameRepository = { + findByName: jest.fn().mockImplementation((name: string) => { + if (name == validName) { + return { + getProps: jest.fn().mockImplementation(() => ({ + userId: validUserId, + })), + }; + } + if (name == furtherInvalidName) { + return { + getProps: jest.fn().mockImplementation(() => ({ + userId: invalidUserId, + })), + }; + } + throw new Error(); + }), +}; + +const mockAuthenticationEntity = { + id: validUserId, + authenticate: jest + .fn() + .mockImplementation((password: string) => password === validPassword), +}; + +const mockAuthenticationRepository = { + findOneById: jest.fn().mockImplementation((id: string) => { + if (id === validUserId) return mockAuthenticationEntity; + throw new Error(); + }), +}; + +describe('Validate Authentication Query Handler', () => { + let validateAuthenticationQueryHandler: ValidateAuthenticationQueryHandler; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: AUTHENTICATION_REPOSITORY, + useValue: mockAuthenticationRepository, + }, + { + provide: USERNAME_REPOSITORY, + useValue: mockUsernameRepository, + }, + ValidateAuthenticationQueryHandler, + ], + }).compile(); + + validateAuthenticationQueryHandler = + module.get( + ValidateAuthenticationQueryHandler, + ); + }); + + it('should be defined', () => { + expect(validateAuthenticationQueryHandler).toBeDefined(); + }); + + describe('execution', () => { + it('should validate an authentication query', async () => { + const validateAuthenticationQuery = new ValidateAuthenticationQuery( + validName, + validPassword, + ); + const id: AggregateID = await validateAuthenticationQueryHandler.execute( + validateAuthenticationQuery, + ); + expect(id).toBe(validUserId); + }); + it('should not validate an authentication query if username is invalid', async () => { + const validateAuthenticationQuery = new ValidateAuthenticationQuery( + 'invalidName', + validPassword, + ); + await expect( + validateAuthenticationQueryHandler.execute(validateAuthenticationQuery), + ).rejects.toBeInstanceOf(NotFoundException); + }); + it('should not validate an authentication query if authentication is not found', async () => { + const validateAuthenticationQuery = new ValidateAuthenticationQuery( + furtherInvalidName, + validPassword, + ); + await expect( + validateAuthenticationQueryHandler.execute(validateAuthenticationQuery), + ).rejects.toBeInstanceOf(NotFoundException); + }); + it('should not validate a failing authentication query', async () => { + const validateAuthenticationQuery = new ValidateAuthenticationQuery( + validName, + invalidPassword, + ); + await expect( + validateAuthenticationQueryHandler.execute(validateAuthenticationQuery), + ).rejects.toBeInstanceOf(UnauthorizedException); + }); + }); +}); diff --git a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts index 23842f9..a9b08b9 100644 --- a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts +++ b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts @@ -59,6 +59,11 @@ describe('Username repository', () => { mockMessagePublisher, ); }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + it('should be defined', () => { expect(usernameRepository).toBeDefined(); }); @@ -75,6 +80,17 @@ describe('Username repository', () => { }); expect(username.getProps().name).toBe('john.doe@email.com'); }); + it('should find a username by its name', async () => { + jest.spyOn(usernameRepository, 'findOne'); + const username: UsernameEntity = await usernameRepository.findByName( + 'john.doe@email.com', + ); + expect(usernameRepository.findOne).toHaveBeenCalledTimes(1); + expect(usernameRepository.findOne).toHaveBeenCalledWith({ + username: 'john.doe@email.com', + }); + expect(username.getProps().name).toBe('john.doe@email.com'); + }); it('should update a username', async () => { jest.spyOn(usernameRepository, 'updateWhere'); const usernameToUpdate: UsernameEntity = await UsernameEntity.create({ diff --git a/src/modules/authentication/tests/unit/interface/validate-authentication.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/validate-authentication.grpc.controller.spec.ts new file mode 100644 index 0000000..e6babb3 --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/validate-authentication.grpc.controller.spec.ts @@ -0,0 +1,75 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { ValidateAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/validate-authentication.request.dto'; +import { ValidateAuthenticationGrpcController } from '@modules/authentication/interface/grpc-controllers/validate-authentication.grpc.controller'; +import { QueryBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const validateAuthenticationRequest: ValidateAuthenticationRequestDto = { + password: 'John123', + name: 'john.doe@email.com', +}; + +const mockQueryBus = { + execute: jest + .fn() + .mockImplementationOnce(() => '78153e03-4861-4f58-a705-88526efee53b') + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Validate Authentication Grpc Controller', () => { + let validateAuthenticationGrpcController: ValidateAuthenticationGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: QueryBus, + useValue: mockQueryBus, + }, + ValidateAuthenticationGrpcController, + ], + }).compile(); + + validateAuthenticationGrpcController = + module.get( + ValidateAuthenticationGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(validateAuthenticationGrpcController).toBeDefined(); + }); + + it('should validate an authentication', async () => { + jest.spyOn(mockQueryBus, 'execute'); + const result: IdResponse = + await validateAuthenticationGrpcController.validate( + validateAuthenticationRequest, + ); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('78153e03-4861-4f58-a705-88526efee53b'); + expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if authentication fails', async () => { + jest.spyOn(mockQueryBus, 'execute'); + expect.assertions(3); + try { + await validateAuthenticationGrpcController.validate( + validateAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.PERMISSION_DENIED); + } + expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); + }); +}); From 85746fde481abedc1e3da290ddb2d10135dee8af Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 11 Jul 2023 15:16:11 +0200 Subject: [PATCH 15/23] user updated message handler --- src/app.module.ts | 10 ++ .../authentication/authentication.mapper.ts | 2 - .../authentication/authentication.module.ts | 4 + .../authentication.repository.ts | 6 +- .../infrastructure/username.repository.ts | 19 +++- .../user-updated.message-handler.ts | 42 ++++++++ .../username.repository.spec.ts | 4 +- .../user-updated.message-handler.spec.ts | 100 ++++++++++++++++++ .../tests/unit/username.mapper.spec.ts | 12 ++- src/modules/authentication/username.mapper.ts | 20 ++-- 10 files changed, 196 insertions(+), 23 deletions(-) create mode 100644 src/modules/authentication/interface/message-handlers/user-updated.message-handler.ts create mode 100644 src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts diff --git a/src/app.module.ts b/src/app.module.ts index adaf99c..96860ca 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -24,6 +24,16 @@ import { uri: configService.get('MESSAGE_BROKER_URI'), exchange: configService.get('MESSAGE_BROKER_EXCHANGE'), name: 'auth', + handlers: { + userUpdated: { + routingKey: 'user.updated', + queue: 'auth-user-updated', + }, + userDeleted: { + routingKey: 'user.deleted', + queue: 'auth-user-deleted', + }, + }, }), }), // AutomapperModule.forRoot({ strategyInitializer: classes() }), diff --git a/src/modules/authentication/authentication.mapper.ts b/src/modules/authentication/authentication.mapper.ts index 7566f68..25c46bd 100644 --- a/src/modules/authentication/authentication.mapper.ts +++ b/src/modules/authentication/authentication.mapper.ts @@ -39,8 +39,6 @@ export class AuthenticationMapper })), } : undefined, - createdAt: copy.createdAt, - updatedAt: copy.updatedAt, }; return record; }; diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 539f55b..71b9b4c 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -25,6 +25,7 @@ import { UpdatePasswordGrpcController } from './interface/grpc-controllers/updat import { UpdatePasswordService } from './core/application/commands/update-password/update-password.service'; import { ValidateAuthenticationGrpcController } from './interface/grpc-controllers/validate-authentication.grpc.controller'; import { ValidateAuthenticationQueryHandler } from './core/application/queries/validate-authentication/validate-authentication.query-handler'; +import { UserUpdatedMessageHandler } from './interface/message-handlers/user-updated.message-handler'; const grpcControllers = [ CreateAuthenticationGrpcController, @@ -36,6 +37,8 @@ const grpcControllers = [ ValidateAuthenticationGrpcController, ]; +const messageHandlers = [UserUpdatedMessageHandler]; + const commandHandlers: Provider[] = [ CreateAuthenticationService, DeleteAuthenticationService, @@ -73,6 +76,7 @@ const orms: Provider[] = [PrismaService]; imports: [CqrsModule], controllers: [...grpcControllers], providers: [ + ...messageHandlers, ...commandHandlers, ...queryHandlers, ...mappers, diff --git a/src/modules/authentication/infrastructure/authentication.repository.ts b/src/modules/authentication/infrastructure/authentication.repository.ts index f176613..fc150a1 100644 --- a/src/modules/authentication/infrastructure/authentication.repository.ts +++ b/src/modules/authentication/infrastructure/authentication.repository.ts @@ -11,15 +11,15 @@ import { PrismaService } from './prisma.service'; import { AuthenticationMapper } from '../authentication.mapper'; import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; -export type AuthenticationBaseModel = { +type AuthenticationBaseModel = { uuid: string; password: string; - createdAt: Date; - updatedAt: Date; }; export type AuthenticationReadModel = AuthenticationBaseModel & { usernames: UsernameModel[]; + createdAt: Date; + updatedAt: Date; }; export type AuthenticationWriteModel = AuthenticationBaseModel & { diff --git a/src/modules/authentication/infrastructure/username.repository.ts b/src/modules/authentication/infrastructure/username.repository.ts index 9342bdd..095962a 100644 --- a/src/modules/authentication/infrastructure/username.repository.ts +++ b/src/modules/authentication/infrastructure/username.repository.ts @@ -12,20 +12,29 @@ import { UsernameRepositoryPort } from '../core/application/ports/username.repos import { UsernameMapper } from '../username.mapper'; import { Type } from '../core/domain/username.types'; -export type UsernameModel = { +type UsernameBaseModel = { username: string; authUuid: string; type: string; - createdAt: Date; - updatedAt: Date; }; +export type UsernameReadModel = UsernameBaseModel & { + createdAt?: Date; + updatedAt?: Date; +}; + +export type UsernameWriteModel = UsernameBaseModel; + /** * Repository is used for retrieving/saving domain entities * */ @Injectable() export class UsernameRepository - extends PrismaRepositoryBase + extends PrismaRepositoryBase< + UsernameEntity, + UsernameReadModel, + UsernameWriteModel + > implements UsernameRepositoryPort { constructor( @@ -42,7 +51,7 @@ export class UsernameRepository eventEmitter, new LoggerBase({ logger: new Logger(UsernameRepository.name), - domain: 'auth', + domain: 'auth.username', messagePublisher, }), ); diff --git a/src/modules/authentication/interface/message-handlers/user-updated.message-handler.ts b/src/modules/authentication/interface/message-handlers/user-updated.message-handler.ts new file mode 100644 index 0000000..55a280b --- /dev/null +++ b/src/modules/authentication/interface/message-handlers/user-updated.message-handler.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { RabbitSubscribe } from '@mobicoop/message-broker-module'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { UpdateUsernameCommand } from '@modules/authentication/core/application/commands/update-username/update-username.command'; + +@Injectable() +export class UserUpdatedMessageHandler { + constructor(private readonly commandBus: CommandBus) {} + + @RabbitSubscribe({ + name: 'userUpdated', + }) + public async userUpdated(message: string) { + const updatedUser = JSON.parse(message); + try { + if (!updatedUser.hasOwnProperty('userId')) throw new Error(); + if (updatedUser.hasOwnProperty('email') && updatedUser.email) { + await this.commandBus.execute( + new UpdateUsernameCommand({ + userId: updatedUser.userId, + username: { + name: updatedUser.email, + type: Type.EMAIL, + }, + }), + ); + } + if (updatedUser.hasOwnProperty('phone') && updatedUser.phone) { + await this.commandBus.execute( + new UpdateUsernameCommand({ + userId: updatedUser.userId, + username: { + name: updatedUser.phone, + type: Type.PHONE, + }, + }), + ); + } + } catch (e: any) {} + } +} diff --git a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts index a9b08b9..7a58e72 100644 --- a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts +++ b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@modules/authentication/infrastructure/prisma.ser import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; import { - UsernameModel, + UsernameReadModel, UsernameRepository, } from '@modules/authentication/infrastructure/username.repository'; import { Type } from '@modules/authentication/core/domain/username.types'; @@ -13,7 +13,7 @@ const mockPrismaService = { username: { findFirst: jest.fn().mockImplementation(async () => { const now = new Date('2023-06-21 06:00:00'); - const record: UsernameModel = { + const record: UsernameReadModel = { authUuid: '330bd6de-1eb8-450b-8674-0e3c9209f048', type: Type.EMAIL, username: 'john.doe@email.com', diff --git a/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts b/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts new file mode 100644 index 0000000..5f490e7 --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts @@ -0,0 +1,100 @@ +import { UserUpdatedMessageHandler } from '@modules/authentication/interface/message-handlers/user-updated.message-handler'; +import { CommandBus } from '@nestjs/cqrs'; +import { Test, TestingModule } from '@nestjs/testing'; + +const userEmailUpdatedMessage = + '{"userId":"2436d413-b7c7-429e-9792-b78edc17b3ca","email":"new-john.doe@email.com"}'; + +const userPhoneUpdatedMessage = + '{"userId":"2436d413-b7c7-429e-9792-b78edc17b3ca","phone":"+33611224455"}'; + +const userBirthDateUpdatedMessage = + '{"userId":"2436d413-b7c7-429e-9792-b78edc17b3ca","birthDate":"1976-10-23"}'; + +const userIdNotProvidedUpdatedMessage = + '{"user":"2436d413-b7c7-429e-9792-b78edc17b300","email":"new-john.doe@email.com"}'; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => 'new-john.doe@email.com') + .mockImplementationOnce(() => '+33611224455'), +}; + +describe('User Updated Message Handler', () => { + let userUpdatedMessageHandler: UserUpdatedMessageHandler; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + UserUpdatedMessageHandler, + ], + }).compile(); + + userUpdatedMessageHandler = module.get( + UserUpdatedMessageHandler, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(userUpdatedMessageHandler).toBeDefined(); + }); + + it('should update an email username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await userUpdatedMessageHandler.userUpdated(userEmailUpdatedMessage); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should update a phone username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await userUpdatedMessageHandler.userUpdated(userPhoneUpdatedMessage); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should not update a username if message does not contain email nor phone', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await userUpdatedMessageHandler.userUpdated(userBirthDateUpdatedMessage); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(0); + }); + + it('should not update a username if userId is unknown', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await userUpdatedMessageHandler.userUpdated( + userIdNotProvidedUpdatedMessage, + ); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(0); + }); + + // it('should throw a dedicated RpcException if username already exists', async () => { + // jest.spyOn(mockCommandBus, 'execute'); + // expect.assertions(3); + // try { + // await updateUsernameGrpcController.updateUsername(updateUsernameRequest); + // } catch (e: any) { + // expect(e).toBeInstanceOf(RpcException); + // expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); + // } + // expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + // }); + + // it('should throw a generic RpcException', async () => { + // jest.spyOn(mockCommandBus, 'execute'); + // expect.assertions(3); + // try { + // await updateUsernameGrpcController.updateUsername(updateUsernameRequest); + // } catch (e: any) { + // expect(e).toBeInstanceOf(RpcException); + // expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + // } + // expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + // }); +}); diff --git a/src/modules/authentication/tests/unit/username.mapper.spec.ts b/src/modules/authentication/tests/unit/username.mapper.spec.ts index c7250e3..d5ff644 100644 --- a/src/modules/authentication/tests/unit/username.mapper.spec.ts +++ b/src/modules/authentication/tests/unit/username.mapper.spec.ts @@ -1,6 +1,9 @@ import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; import { Type } from '@modules/authentication/core/domain/username.types'; -import { UsernameModel } from '@modules/authentication/infrastructure/username.repository'; +import { + UsernameReadModel, + UsernameWriteModel, +} from '@modules/authentication/infrastructure/username.repository'; import { UsernameResponseDto } from '@modules/authentication/interface/dtos/username.response.dto'; import { UsernameMapper } from '@modules/authentication/username.mapper'; import { Test } from '@nestjs/testing'; @@ -16,7 +19,7 @@ const usernameEntity: UsernameEntity = new UsernameEntity({ createdAt: now, updatedAt: now, }); -const usernameModel: UsernameModel = { +const usernameReadModel: UsernameReadModel = { authUuid: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e', username: 'john.doe@email.com', type: Type.EMAIL, @@ -39,13 +42,14 @@ describe('Username Mapper', () => { }); it('should map domain entity to persistence data', async () => { - const mapped: UsernameModel = usernameMapper.toPersistence(usernameEntity); + const mapped: UsernameWriteModel = + usernameMapper.toPersistence(usernameEntity); expect(mapped.username).toBe('john.doe@email.com'); expect(mapped.type).toBe(Type.EMAIL); }); it('should map persisted data to domain entity', async () => { - const mapped: UsernameEntity = usernameMapper.toDomain(usernameModel); + const mapped: UsernameEntity = usernameMapper.toDomain(usernameReadModel); expect(mapped.getProps().name).toBe('john.doe@email.com'); }); diff --git a/src/modules/authentication/username.mapper.ts b/src/modules/authentication/username.mapper.ts index 33d399b..7d22fc8 100644 --- a/src/modules/authentication/username.mapper.ts +++ b/src/modules/authentication/username.mapper.ts @@ -2,7 +2,10 @@ import { Mapper } from '@mobicoop/ddd-library'; import { Injectable } from '@nestjs/common'; import { Type } from './core/domain/username.types'; import { UsernameEntity } from './core/domain/username.entity'; -import { UsernameModel } from './infrastructure/username.repository'; +import { + UsernameReadModel, + UsernameWriteModel, +} from './infrastructure/username.repository'; import { UsernameResponseDto } from './interface/dtos/username.response.dto'; /** @@ -15,21 +18,24 @@ import { UsernameResponseDto } from './interface/dtos/username.response.dto'; @Injectable() export class UsernameMapper implements - Mapper + Mapper< + UsernameEntity, + UsernameReadModel, + UsernameWriteModel, + UsernameResponseDto + > { - toPersistence = (entity: UsernameEntity): UsernameModel => { + toPersistence = (entity: UsernameEntity): UsernameWriteModel => { const copy = entity.getProps(); - const record: UsernameModel = { + const record: UsernameWriteModel = { authUuid: copy.userId, username: copy.name, type: copy.type, - createdAt: copy.createdAt, - updatedAt: copy.updatedAt, }; return record; }; - toDomain = (record: UsernameModel): UsernameEntity => { + toDomain = (record: UsernameReadModel): UsernameEntity => { const entity = new UsernameEntity({ id: record.username, createdAt: new Date(record.createdAt), From ced57e35a6f34d1ea56e359b02b3b3d4f66c2a58 Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 11 Jul 2023 15:29:19 +0200 Subject: [PATCH 16/23] user deleted message handler --- .../authentication/authentication.module.ts | 3 +- .../user-deleted.message-handler.ts | 24 +++++++++ .../user-deleted.message-handler.spec.ts | 54 +++++++++++++++++++ .../user-updated.message-handler.spec.ts | 26 +-------- 4 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 src/modules/authentication/interface/message-handlers/user-deleted.message-handler.ts create mode 100644 src/modules/authentication/tests/unit/interface/user-deleted.message-handler.spec.ts diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 71b9b4c..9b5ad69 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -26,6 +26,7 @@ import { UpdatePasswordService } from './core/application/commands/update-passwo import { ValidateAuthenticationGrpcController } from './interface/grpc-controllers/validate-authentication.grpc.controller'; import { ValidateAuthenticationQueryHandler } from './core/application/queries/validate-authentication/validate-authentication.query-handler'; import { UserUpdatedMessageHandler } from './interface/message-handlers/user-updated.message-handler'; +import { UserDeletedMessageHandler } from './interface/message-handlers/user-deleted.message-handler'; const grpcControllers = [ CreateAuthenticationGrpcController, @@ -37,7 +38,7 @@ const grpcControllers = [ ValidateAuthenticationGrpcController, ]; -const messageHandlers = [UserUpdatedMessageHandler]; +const messageHandlers = [UserUpdatedMessageHandler, UserDeletedMessageHandler]; const commandHandlers: Provider[] = [ CreateAuthenticationService, diff --git a/src/modules/authentication/interface/message-handlers/user-deleted.message-handler.ts b/src/modules/authentication/interface/message-handlers/user-deleted.message-handler.ts new file mode 100644 index 0000000..ccbe54d --- /dev/null +++ b/src/modules/authentication/interface/message-handlers/user-deleted.message-handler.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { RabbitSubscribe } from '@mobicoop/message-broker-module'; +import { DeleteAuthenticationCommand } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.command'; + +@Injectable() +export class UserDeletedMessageHandler { + constructor(private readonly commandBus: CommandBus) {} + + @RabbitSubscribe({ + name: 'userDeleted', + }) + public async userDeleted(message: string) { + const deletedUser = JSON.parse(message); + try { + if (!deletedUser.hasOwnProperty('userId')) throw new Error(); + await this.commandBus.execute( + new DeleteAuthenticationCommand({ + userId: deletedUser.userId, + }), + ); + } catch (e: any) {} + } +} diff --git a/src/modules/authentication/tests/unit/interface/user-deleted.message-handler.spec.ts b/src/modules/authentication/tests/unit/interface/user-deleted.message-handler.spec.ts new file mode 100644 index 0000000..17e8c1d --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/user-deleted.message-handler.spec.ts @@ -0,0 +1,54 @@ +import { UserDeletedMessageHandler } from '@modules/authentication/interface/message-handlers/user-deleted.message-handler'; +import { CommandBus } from '@nestjs/cqrs'; +import { Test, TestingModule } from '@nestjs/testing'; + +const userDeletedMessage = '{"userId":"2436d413-b7c7-429e-9792-b78edc17b3ca"}'; + +const userIdNotProvidedDeletedMessage = + '{"user":"2436d413-b7c7-429e-9792-b78edc17b300"}'; + +const mockCommandBus = { + execute: jest.fn().mockImplementationOnce(() => 'john.doe@email.com'), +}; + +describe('User Deleted Message Handler', () => { + let userDeletedMessageHandler: UserDeletedMessageHandler; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + UserDeletedMessageHandler, + ], + }).compile(); + + userDeletedMessageHandler = module.get( + UserDeletedMessageHandler, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(userDeletedMessageHandler).toBeDefined(); + }); + + it('should delete an authentication', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await userDeletedMessageHandler.userDeleted(userDeletedMessage); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should not update an authentication if userId is not provided', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await userDeletedMessageHandler.userDeleted( + userIdNotProvidedDeletedMessage, + ); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(0); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts b/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts index 5f490e7..6b18907 100644 --- a/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts +++ b/src/modules/authentication/tests/unit/interface/user-updated.message-handler.spec.ts @@ -66,35 +66,11 @@ describe('User Updated Message Handler', () => { expect(mockCommandBus.execute).toHaveBeenCalledTimes(0); }); - it('should not update a username if userId is unknown', async () => { + it('should not update a username if userId is not provided', async () => { jest.spyOn(mockCommandBus, 'execute'); await userUpdatedMessageHandler.userUpdated( userIdNotProvidedUpdatedMessage, ); expect(mockCommandBus.execute).toHaveBeenCalledTimes(0); }); - - // it('should throw a dedicated RpcException if username already exists', async () => { - // jest.spyOn(mockCommandBus, 'execute'); - // expect.assertions(3); - // try { - // await updateUsernameGrpcController.updateUsername(updateUsernameRequest); - // } catch (e: any) { - // expect(e).toBeInstanceOf(RpcException); - // expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); - // } - // expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); - // }); - - // it('should throw a generic RpcException', async () => { - // jest.spyOn(mockCommandBus, 'execute'); - // expect.assertions(3); - // try { - // await updateUsernameGrpcController.updateUsername(updateUsernameRequest); - // } catch (e: any) { - // expect(e).toBeInstanceOf(RpcException); - // expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); - // } - // expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); - // }); }); From d78a065c54f54d32931c4db1d0a511ee9cc67d89 Mon Sep 17 00:00:00 2001 From: sbriat Date: Wed, 12 Jul 2023 10:39:55 +0200 Subject: [PATCH 17/23] authorization module --- opa/user/delete.rego | 2 +- opa/user/read.rego | 2 +- opa/user/update.rego | 2 +- src/app.module.ts | 11 +- src/main.ts | 7 +- .../authentication/authentication.module.ts | 1 + .../primaries/authorization.controller.ts | 43 -- .../primaries/authorization.presenter.ts | 6 - .../adapters/secondaries/decision-result.ts | 3 - .../adapters/secondaries/decision.ts | 6 - .../authorization/authorization.di-tokens.ts | 1 + .../authorization/authorization.module.ts | 30 +- .../application/ports/decision-maker.port.ts | 11 + .../decision/decision.query-handler.ts | 20 + .../queries/decision/decision.query.ts | 19 + .../domain/authorization.types.ts} | 11 + .../authorization/domain/dtos/context-item.ts | 9 - .../domain/dtos/decision.request.ts | 17 - .../authorization/domain/dtos/domain.enum.ts | 5 - .../domain/entities/authorization.ts | 10 - .../domain/interfaces/decision-maker.ts | 13 - .../domain/usecases/decision.usecase.ts | 16 - .../opa.decision-maker.ts | 31 +- .../interface/dtos/decision.response.dto.ts | 7 + .../grpc-controllers}/authorization.proto | 0 .../decide.grpc.controller.ts | 33 + .../dtos/decision.request.dto.ts | 19 + .../mappers/authorization.profile.ts | 18 - .../authorization/queries/decision.query.ts | 15 - .../unit/core/decision.query-handler.spec.ts | 74 +++ .../tests/unit/decision.usecase.spec.ts | 57 -- .../infrastructure/opa.decision-maker.spec.ts | 123 ++++ .../interface/decide.grpc.controller.spec.ts | 91 +++ .../tests/unit/opa.decision-maker.spec.ts | 97 --- .../secondaries/prisma-repository.abstract.ts | 259 -------- .../adapters/secondaries/prisma-service.ts | 15 - src/modules/database/database.module.ts | 10 - .../database/domain/auth-repository.ts | 3 - .../database/exceptions/database.exception.ts | 24 - .../interfaces/collection.interface.ts | 4 - .../interfaces/repository.interface.ts | 17 - .../tests/unit/prisma-repository.spec.ts | 571 ------------------ .../adapters/primaries/health.controller.ts | 35 -- .../adapters/secondaries/message-broker.ts | 12 - .../health/adapters/secondaries/messager.ts | 18 - .../ports/check-repository.port.ts | 3 + .../repositories.health-indicator.usecase.ts | 54 ++ .../prisma.health-indicator.usecase.ts | 25 - src/modules/health/health.constants.ts | 1 + src/modules/health/health.di-tokens.ts | 2 + src/modules/health/health.module.ts | 69 ++- .../health.grpc.controller.ts} | 19 +- .../grpc-controllers}/health.proto | 0 .../health.http.controller.ts | 28 + .../tests/unit/health.grpc.controller.spec.ts | 72 +++ .../tests/unit/health.http.controller.spec.ts | 90 +++ .../health/tests/unit/messager.spec.ts | 47 -- .../prisma.health-indicator.usecase.spec.ts | 58 -- ...ositories.health-indicator.usecase.spec.ts | 84 +++ .../authentication-messager.controller.ts | 52 -- .../primaries/authentication.controller.ts | 188 ------ .../primaries/authentication.presenter.ts | 6 - .../adapters/primaries/authentication.proto | 42 -- .../adapters/primaries/username.presenter.ts | 9 - .../secondaries/authentication.repository.ts | 8 - .../adapters/secondaries/messager.ts | 18 - .../secondaries/username.repository.ts | 8 - .../authentication.module.ts | 66 -- .../commands/add-username.command.ts | 9 - .../commands/create-authentication.command.ts | 9 - .../commands/delete-authentication.command.ts | 9 - .../commands/delete-username.command.ts | 9 - .../commands/update-password.command.ts | 9 - .../commands/update-username.command.ts | 9 - .../domain/dtos/add-username.request.ts | 20 - .../dtos/create-authentication.request.ts | 25 - .../dtos/delete-authentication.request.ts | 9 - .../domain/dtos/delete-username.request.ts | 9 - .../domain/dtos/type.enum.ts | 4 - .../domain/dtos/update-password.request.ts | 14 - .../domain/dtos/update-username.request.ts | 20 - .../dtos/validate-authentication.request.ts | 11 - .../domain/entities/authentication.ts | 8 - .../domain/entities/username.ts | 13 - .../domain/interfaces/message-broker.ts | 12 - .../domain/usecases/add-username.usecase.ts | 33 - .../usecases/create-authentication.usecase.ts | 46 -- .../usecases/delete-authentication.usecase.ts | 37 -- .../usecases/delete-username.usecase.ts | 38 -- .../usecases/update-password.usecase.ts | 34 -- .../usecases/update-username.usecase.ts | 67 -- .../validate-authentication.usecase.ts | 41 -- .../mappers/authentication.profile.ts | 18 - .../mappers/username.profile.ts | 21 - .../queries/validate-authentication.query.ts | 9 - .../authentication.repository.spec.ts | 162 ----- .../integration/username.repository.spec.ts | 282 --------- .../tests/unit/add-username.usecase.spec.ts | 79 --- .../create-authentication.usecase.spec.ts | 99 --- .../delete-authentication.usecase.spec.ts | 102 ---- .../unit/delete-username.usecase.spec.ts | 115 ---- .../tests/unit/messager.spec.ts | 47 -- .../unit/update-password.usecase.spec.ts | 81 --- .../unit/update-username.usecase.spec.ts | 151 ----- .../validate-authentication.usecase.spec.ts | 107 ---- .../unit/rpc-validation-pipe.usecase.spec.ts | 6 +- 106 files changed, 847 insertions(+), 3654 deletions(-) delete mode 100644 src/modules/authorization/adapters/primaries/authorization.controller.ts delete mode 100644 src/modules/authorization/adapters/primaries/authorization.presenter.ts delete mode 100644 src/modules/authorization/adapters/secondaries/decision-result.ts delete mode 100644 src/modules/authorization/adapters/secondaries/decision.ts create mode 100644 src/modules/authorization/authorization.di-tokens.ts create mode 100644 src/modules/authorization/core/application/ports/decision-maker.port.ts create mode 100644 src/modules/authorization/core/application/queries/decision/decision.query-handler.ts create mode 100644 src/modules/authorization/core/application/queries/decision/decision.query.ts rename src/modules/authorization/{domain/dtos/action.enum.ts => core/domain/authorization.types.ts} (50%) delete mode 100644 src/modules/authorization/domain/dtos/context-item.ts delete mode 100644 src/modules/authorization/domain/dtos/decision.request.ts delete mode 100644 src/modules/authorization/domain/dtos/domain.enum.ts delete mode 100644 src/modules/authorization/domain/entities/authorization.ts delete mode 100644 src/modules/authorization/domain/interfaces/decision-maker.ts delete mode 100644 src/modules/authorization/domain/usecases/decision.usecase.ts rename src/modules/authorization/{adapters/secondaries => infrastructure}/opa.decision-maker.ts (54%) create mode 100644 src/modules/authorization/interface/dtos/decision.response.dto.ts rename src/modules/authorization/{adapters/primaries => interface/grpc-controllers}/authorization.proto (100%) create mode 100644 src/modules/authorization/interface/grpc-controllers/decide.grpc.controller.ts create mode 100644 src/modules/authorization/interface/grpc-controllers/dtos/decision.request.dto.ts delete mode 100644 src/modules/authorization/mappers/authorization.profile.ts delete mode 100644 src/modules/authorization/queries/decision.query.ts create mode 100644 src/modules/authorization/tests/unit/core/decision.query-handler.spec.ts delete mode 100644 src/modules/authorization/tests/unit/decision.usecase.spec.ts create mode 100644 src/modules/authorization/tests/unit/infrastructure/opa.decision-maker.spec.ts create mode 100644 src/modules/authorization/tests/unit/interface/decide.grpc.controller.spec.ts delete mode 100644 src/modules/authorization/tests/unit/opa.decision-maker.spec.ts delete mode 100644 src/modules/database/adapters/secondaries/prisma-repository.abstract.ts delete mode 100644 src/modules/database/adapters/secondaries/prisma-service.ts delete mode 100644 src/modules/database/database.module.ts delete mode 100644 src/modules/database/domain/auth-repository.ts delete mode 100644 src/modules/database/exceptions/database.exception.ts delete mode 100644 src/modules/database/interfaces/collection.interface.ts delete mode 100644 src/modules/database/interfaces/repository.interface.ts delete mode 100644 src/modules/database/tests/unit/prisma-repository.spec.ts delete mode 100644 src/modules/health/adapters/primaries/health.controller.ts delete mode 100644 src/modules/health/adapters/secondaries/message-broker.ts delete mode 100644 src/modules/health/adapters/secondaries/messager.ts create mode 100644 src/modules/health/core/application/ports/check-repository.port.ts create mode 100644 src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts delete mode 100644 src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts create mode 100644 src/modules/health/health.constants.ts create mode 100644 src/modules/health/health.di-tokens.ts rename src/modules/health/{adapters/primaries/health-server.controller.ts => interface/grpc-controllers/health.grpc.controller.ts} (56%) rename src/modules/health/{adapters/primaries => interface/grpc-controllers}/health.proto (100%) create mode 100644 src/modules/health/interface/http-controllers/health.http.controller.ts create mode 100644 src/modules/health/tests/unit/health.grpc.controller.spec.ts create mode 100644 src/modules/health/tests/unit/health.http.controller.spec.ts delete mode 100644 src/modules/health/tests/unit/messager.spec.ts delete mode 100644 src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts create mode 100644 src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts delete mode 100644 src/modules/oldauthentication/adapters/primaries/authentication.controller.ts delete mode 100644 src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts delete mode 100644 src/modules/oldauthentication/adapters/primaries/authentication.proto delete mode 100644 src/modules/oldauthentication/adapters/primaries/username.presenter.ts delete mode 100644 src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts delete mode 100644 src/modules/oldauthentication/adapters/secondaries/messager.ts delete mode 100644 src/modules/oldauthentication/adapters/secondaries/username.repository.ts delete mode 100644 src/modules/oldauthentication/authentication.module.ts delete mode 100644 src/modules/oldauthentication/commands/add-username.command.ts delete mode 100644 src/modules/oldauthentication/commands/create-authentication.command.ts delete mode 100644 src/modules/oldauthentication/commands/delete-authentication.command.ts delete mode 100644 src/modules/oldauthentication/commands/delete-username.command.ts delete mode 100644 src/modules/oldauthentication/commands/update-password.command.ts delete mode 100644 src/modules/oldauthentication/commands/update-username.command.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/add-username.request.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/create-authentication.request.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/delete-username.request.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/type.enum.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/update-password.request.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/update-username.request.ts delete mode 100644 src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts delete mode 100644 src/modules/oldauthentication/domain/entities/authentication.ts delete mode 100644 src/modules/oldauthentication/domain/entities/username.ts delete mode 100644 src/modules/oldauthentication/domain/interfaces/message-broker.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/add-username.usecase.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/update-password.usecase.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/update-username.usecase.ts delete mode 100644 src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts delete mode 100644 src/modules/oldauthentication/mappers/authentication.profile.ts delete mode 100644 src/modules/oldauthentication/mappers/username.profile.ts delete mode 100644 src/modules/oldauthentication/queries/validate-authentication.query.ts delete mode 100644 src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts delete mode 100644 src/modules/oldauthentication/tests/integration/username.repository.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/messager.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts delete mode 100644 src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts diff --git a/opa/user/delete.rego b/opa/user/delete.rego index 541466e..1a22dbc 100644 --- a/opa/user/delete.rego +++ b/opa/user/delete.rego @@ -3,7 +3,7 @@ package USER.DELETE default allow := false allow { - input.uuid == input.owner + input.id == input.owner } allow { diff --git a/opa/user/read.rego b/opa/user/read.rego index 843d31b..ac4418c 100644 --- a/opa/user/read.rego +++ b/opa/user/read.rego @@ -3,7 +3,7 @@ package USER.READ default allow := false allow { - input.uuid == input.owner + input.id == input.owner } allow { diff --git a/opa/user/update.rego b/opa/user/update.rego index 6cde81b..cb2ed6c 100644 --- a/opa/user/update.rego +++ b/opa/user/update.rego @@ -3,7 +3,7 @@ package USER.UPDATE default allow := false allow { - input.uuid == input.owner + input.id == input.owner } allow { diff --git a/src/app.module.ts b/src/app.module.ts index 96860ca..52594b7 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,15 +1,13 @@ -// import { classes } from '@automapper/classes'; -// import { AutomapperModule } from '@automapper/nestjs'; import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; -// import { AuthorizationModule } from './modules/authorization/authorization.module'; -// import { HealthModule } from './modules/health/health.module'; import { AuthenticationModule } from '@modules/authentication/authentication.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { MessageBrokerModule, MessageBrokerModuleOptions, } from '@mobicoop/message-broker-module'; +import { HealthModule } from '@modules/health/health.module'; +import { AuthorizationModule } from '@modules/authorization/authorization.module'; @Module({ imports: [ @@ -36,10 +34,9 @@ import { }, }), }), - // AutomapperModule.forRoot({ strategyInitializer: classes() }), AuthenticationModule, - // AuthorizationModule, - // HealthModule, + AuthorizationModule, + HealthModule, ], controllers: [], providers: [], diff --git a/src/main.ts b/src/main.ts index 82277f9..9e21a07 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,9 +19,12 @@ async function bootstrap() { ), join( __dirname, - 'modules/authorization/adapters/primaries/authorization.proto', + 'modules/authorization/interface/grpc-controllers/authorization.proto', + ), + join( + __dirname, + 'modules/health/interface/grpc-controllers/health.proto', ), - join(__dirname, 'modules/health/adapters/primaries/health.proto'), ], url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT, loader: { keepCase: true, enums: String }, diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 9b5ad69..688efcb 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -88,6 +88,7 @@ const orms: Provider[] = [PrismaService]; exports: [ PrismaService, AuthenticationMapper, + UsernameMapper, AUTHENTICATION_REPOSITORY, USERNAME_REPOSITORY, ], diff --git a/src/modules/authorization/adapters/primaries/authorization.controller.ts b/src/modules/authorization/adapters/primaries/authorization.controller.ts deleted file mode 100644 index a3eacd3..0000000 --- a/src/modules/authorization/adapters/primaries/authorization.controller.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Mapper } from '@automapper/core'; -import { InjectMapper } from '@automapper/nestjs'; -import { Controller, UsePipes } from '@nestjs/common'; -import { QueryBus } from '@nestjs/cqrs'; -import { GrpcMethod, RpcException } from '@nestjs/microservices'; -import { RpcValidationPipe } from 'src/utils/pipes/rpc.validation-pipe'; -import { DecisionRequest } from '../../domain/dtos/decision.request'; -import { Authorization } from '../../domain/entities/authorization'; -import { DecisionQuery } from '../../queries/decision.query'; -import { AuthorizationPresenter } from './authorization.presenter'; - -@UsePipes( - new RpcValidationPipe({ - whitelist: true, - forbidUnknownValues: false, - }), -) -@Controller() -export class AuthorizationController { - constructor( - private readonly queryBus: QueryBus, - @InjectMapper() private readonly mapper: Mapper, - ) {} - - @GrpcMethod('AuthorizationService', 'Decide') - async decide(data: DecisionRequest): Promise { - try { - const authorization: Authorization = await this.queryBus.execute( - new DecisionQuery(data.domain, data.action, data.context), - ); - return this.mapper.map( - authorization, - Authorization, - AuthorizationPresenter, - ); - } catch (e) { - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } -} diff --git a/src/modules/authorization/adapters/primaries/authorization.presenter.ts b/src/modules/authorization/adapters/primaries/authorization.presenter.ts deleted file mode 100644 index c6f3733..0000000 --- a/src/modules/authorization/adapters/primaries/authorization.presenter.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { AutoMap } from '@automapper/classes'; - -export class AuthorizationPresenter { - @AutoMap() - allow: boolean; -} diff --git a/src/modules/authorization/adapters/secondaries/decision-result.ts b/src/modules/authorization/adapters/secondaries/decision-result.ts deleted file mode 100644 index 547205b..0000000 --- a/src/modules/authorization/adapters/secondaries/decision-result.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class DecisionResult { - allow: boolean; -} diff --git a/src/modules/authorization/adapters/secondaries/decision.ts b/src/modules/authorization/adapters/secondaries/decision.ts deleted file mode 100644 index ccc5e92..0000000 --- a/src/modules/authorization/adapters/secondaries/decision.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DecisionResult } from './decision-result'; - -export class Decision { - decision_id: string; - result: DecisionResult; -} diff --git a/src/modules/authorization/authorization.di-tokens.ts b/src/modules/authorization/authorization.di-tokens.ts new file mode 100644 index 0000000..4bda93e --- /dev/null +++ b/src/modules/authorization/authorization.di-tokens.ts @@ -0,0 +1 @@ +export const DECISION_MAKER = Symbol('DECISION_MAKER'); diff --git a/src/modules/authorization/authorization.module.ts b/src/modules/authorization/authorization.module.ts index c66d76d..50480a9 100644 --- a/src/modules/authorization/authorization.module.ts +++ b/src/modules/authorization/authorization.module.ts @@ -1,16 +1,26 @@ -import { HttpModule } from '@nestjs/axios'; -import { Module } from '@nestjs/common'; +import { Module, Provider } from '@nestjs/common'; import { CqrsModule } from '@nestjs/cqrs'; -import { DatabaseModule } from '../database/database.module'; -import { AuthorizationController } from './adapters/primaries/authorization.controller'; -import { OpaDecisionMaker } from './adapters/secondaries/opa.decision-maker'; -import { DecisionUseCase } from './domain/usecases/decision.usecase'; -import { AuthorizationProfile } from './mappers/authorization.profile'; +import { DecideGrpcController } from './interface/grpc-controllers/decide.grpc.controller'; +import { DecisionQueryHandler } from './core/application/queries/decision/decision.query-handler'; +import { DECISION_MAKER } from './authorization.di-tokens'; +import { OpaDecisionMaker } from './infrastructure/opa.decision-maker'; +import { HttpModule } from '@nestjs/axios'; + +const grpcControllers = [DecideGrpcController]; + +const queryHandlers: Provider[] = [DecisionQueryHandler]; + +const adapters: Provider[] = [ + { + provide: DECISION_MAKER, + useClass: OpaDecisionMaker, + }, +]; @Module({ - imports: [DatabaseModule, CqrsModule, HttpModule], + imports: [CqrsModule, HttpModule], + controllers: [...grpcControllers], + providers: [...queryHandlers, ...adapters], exports: [], - controllers: [AuthorizationController], - providers: [OpaDecisionMaker, DecisionUseCase, AuthorizationProfile], }) export class AuthorizationModule {} diff --git a/src/modules/authorization/core/application/ports/decision-maker.port.ts b/src/modules/authorization/core/application/ports/decision-maker.port.ts new file mode 100644 index 0000000..0fb094d --- /dev/null +++ b/src/modules/authorization/core/application/ports/decision-maker.port.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { Action, ContextItem, Domain } from '../../domain/authorization.types'; + +@Injectable() +export abstract class DecisionMakerPort { + abstract decide( + domain: Domain, + action: Action, + context: ContextItem[], + ): Promise; +} diff --git a/src/modules/authorization/core/application/queries/decision/decision.query-handler.ts b/src/modules/authorization/core/application/queries/decision/decision.query-handler.ts new file mode 100644 index 0000000..cc16468 --- /dev/null +++ b/src/modules/authorization/core/application/queries/decision/decision.query-handler.ts @@ -0,0 +1,20 @@ +import { IQueryHandler, QueryHandler } from '@nestjs/cqrs'; +import { Inject } from '@nestjs/common'; +import { DecisionQuery } from './decision.query'; +import { DECISION_MAKER } from '@modules/authorization/authorization.di-tokens'; +import { DecisionMakerPort } from '../../ports/decision-maker.port'; + +@QueryHandler(DecisionQuery) +export class DecisionQueryHandler implements IQueryHandler { + constructor( + @Inject(DECISION_MAKER) + private readonly decisionMaker: DecisionMakerPort, + ) {} + + execute = async (decisionQuery: DecisionQuery): Promise => + this.decisionMaker.decide( + decisionQuery.domain, + decisionQuery.action, + decisionQuery.context, + ); +} diff --git a/src/modules/authorization/core/application/queries/decision/decision.query.ts b/src/modules/authorization/core/application/queries/decision/decision.query.ts new file mode 100644 index 0000000..0e7a821 --- /dev/null +++ b/src/modules/authorization/core/application/queries/decision/decision.query.ts @@ -0,0 +1,19 @@ +import { QueryBase } from '@mobicoop/ddd-library'; +import { + Action, + ContextItem, + Domain, +} from '@modules/authorization/core/domain/authorization.types'; + +export class DecisionQuery extends QueryBase { + readonly domain: Domain; + readonly action: Action; + readonly context?: ContextItem[]; + + constructor(domain: Domain, action: Action, context?: ContextItem[]) { + super(); + this.domain = domain; + this.action = action; + this.context = context; + } +} diff --git a/src/modules/authorization/domain/dtos/action.enum.ts b/src/modules/authorization/core/domain/authorization.types.ts similarity index 50% rename from src/modules/authorization/domain/dtos/action.enum.ts rename to src/modules/authorization/core/domain/authorization.types.ts index 6e08612..f79dae8 100644 --- a/src/modules/authorization/domain/dtos/action.enum.ts +++ b/src/modules/authorization/core/domain/authorization.types.ts @@ -1,3 +1,14 @@ +export interface ContextItem { + name: string; + value: any; +} + +export enum Domain { + USER = 'USER', + ADMIN = 'ADMIN', + AD = 'AD', +} + export enum Action { CREATE = 'CREATE', READ = 'READ', diff --git a/src/modules/authorization/domain/dtos/context-item.ts b/src/modules/authorization/domain/dtos/context-item.ts deleted file mode 100644 index b9b95dc..0000000 --- a/src/modules/authorization/domain/dtos/context-item.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class ContextItem { - name: string; - value: any; - - constructor(name: string, value: any) { - this.name = name; - this.value = value; - } -} diff --git a/src/modules/authorization/domain/dtos/decision.request.ts b/src/modules/authorization/domain/dtos/decision.request.ts deleted file mode 100644 index 7ab1da6..0000000 --- a/src/modules/authorization/domain/dtos/decision.request.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IsArray, IsEnum, IsNotEmpty } from 'class-validator'; -import { ContextItem } from './context-item'; -import { Action } from './action.enum'; -import { Domain } from './domain.enum'; - -export class DecisionRequest { - @IsEnum(Domain) - @IsNotEmpty() - domain: Domain; - - @IsEnum(Action) - @IsNotEmpty() - action: Action; - - @IsArray() - context?: Array; -} diff --git a/src/modules/authorization/domain/dtos/domain.enum.ts b/src/modules/authorization/domain/dtos/domain.enum.ts deleted file mode 100644 index 9d24c49..0000000 --- a/src/modules/authorization/domain/dtos/domain.enum.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Domain { - USER = 'USER', - ADMIN = 'ADMIN', - AD = 'AD', -} diff --git a/src/modules/authorization/domain/entities/authorization.ts b/src/modules/authorization/domain/entities/authorization.ts deleted file mode 100644 index 3869a3b..0000000 --- a/src/modules/authorization/domain/entities/authorization.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { AutoMap } from '@automapper/classes'; - -export class Authorization { - @AutoMap() - allow: boolean; - - constructor(allow: boolean) { - this.allow = allow; - } -} diff --git a/src/modules/authorization/domain/interfaces/decision-maker.ts b/src/modules/authorization/domain/interfaces/decision-maker.ts deleted file mode 100644 index 4492f27..0000000 --- a/src/modules/authorization/domain/interfaces/decision-maker.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Action } from '../dtos/action.enum'; -import { Domain } from '../dtos/domain.enum'; -import { Authorization } from '../entities/authorization'; - -@Injectable() -export abstract class IMakeDecision { - abstract decide( - domain: Domain, - action: Action, - context: Array<{ name: string; value: string }>, - ): Promise; -} diff --git a/src/modules/authorization/domain/usecases/decision.usecase.ts b/src/modules/authorization/domain/usecases/decision.usecase.ts deleted file mode 100644 index a1799fd..0000000 --- a/src/modules/authorization/domain/usecases/decision.usecase.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { QueryHandler } from '@nestjs/cqrs'; -import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker'; -import { DecisionQuery } from '../../queries/decision.query'; -import { Authorization } from '../entities/authorization'; - -@QueryHandler(DecisionQuery) -export class DecisionUseCase { - constructor(private readonly decisionMaker: OpaDecisionMaker) {} - - execute = (decisionQuery: DecisionQuery): Promise => - this.decisionMaker.decide( - decisionQuery.domain, - decisionQuery.action, - decisionQuery.context, - ); -} diff --git a/src/modules/authorization/adapters/secondaries/opa.decision-maker.ts b/src/modules/authorization/infrastructure/opa.decision-maker.ts similarity index 54% rename from src/modules/authorization/adapters/secondaries/opa.decision-maker.ts rename to src/modules/authorization/infrastructure/opa.decision-maker.ts index 9a012fa..160828e 100644 --- a/src/modules/authorization/adapters/secondaries/opa.decision-maker.ts +++ b/src/modules/authorization/infrastructure/opa.decision-maker.ts @@ -2,15 +2,15 @@ import { HttpService } from '@nestjs/axios'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { lastValueFrom } from 'rxjs'; -import { Action } from '../../domain/dtos/action.enum'; -import { Domain } from '../../domain/dtos/domain.enum'; -import { IMakeDecision } from '../../domain/interfaces/decision-maker'; -import { ContextItem } from '../../domain/dtos/context-item'; -import { Decision } from './decision'; -import { Authorization } from '../../domain/entities/authorization'; +import { DecisionMakerPort } from '../core/application/ports/decision-maker.port'; +import { + Action, + ContextItem, + Domain, +} from '../core/domain/authorization.types'; @Injectable() -export class OpaDecisionMaker extends IMakeDecision { +export class OpaDecisionMaker extends DecisionMakerPort { constructor( private readonly configService: ConfigService, private readonly httpService: HttpService, @@ -21,8 +21,8 @@ export class OpaDecisionMaker extends IMakeDecision { decide = async ( domain: Domain, action: Action, - context: Array, - ): Promise => { + context: ContextItem[], + ): Promise => { const reducedContext = context.reduce( (obj, item) => Object.assign(obj, { [item.name]: item.value }), {}, @@ -30,7 +30,7 @@ export class OpaDecisionMaker extends IMakeDecision { try { const { data } = await lastValueFrom( this.httpService.post( - this.configService.get('OPA_URL') + domain + '/' + action, + `${this.configService.get('OPA_URL')}${domain}/${action}`, { input: { ...reducedContext, @@ -38,9 +38,16 @@ export class OpaDecisionMaker extends IMakeDecision { }, ), ); - return new Authorization(data.result.allow); + return data.result.allow; } catch (e) { - return new Authorization(false); + return false; } }; } + +type Decision = { + decision_id: string; + result: { + allow: boolean; + }; +}; diff --git a/src/modules/authorization/interface/dtos/decision.response.dto.ts b/src/modules/authorization/interface/dtos/decision.response.dto.ts new file mode 100644 index 0000000..ac1f398 --- /dev/null +++ b/src/modules/authorization/interface/dtos/decision.response.dto.ts @@ -0,0 +1,7 @@ +export class DecisionResponseDto { + readonly allow: boolean; + + constructor(allow: boolean) { + this.allow = allow; + } +} diff --git a/src/modules/authorization/adapters/primaries/authorization.proto b/src/modules/authorization/interface/grpc-controllers/authorization.proto similarity index 100% rename from src/modules/authorization/adapters/primaries/authorization.proto rename to src/modules/authorization/interface/grpc-controllers/authorization.proto diff --git a/src/modules/authorization/interface/grpc-controllers/decide.grpc.controller.ts b/src/modules/authorization/interface/grpc-controllers/decide.grpc.controller.ts new file mode 100644 index 0000000..a450bac --- /dev/null +++ b/src/modules/authorization/interface/grpc-controllers/decide.grpc.controller.ts @@ -0,0 +1,33 @@ +import { RpcExceptionCode, RpcValidationPipe } from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { QueryBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { DecisionRequestDto } from './dtos/decision.request.dto'; +import { DecisionQuery } from '@modules/authorization/core/application/queries/decision/decision.query'; +import { DecisionResponseDto } from '../dtos/decision.response.dto'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class DecideGrpcController { + constructor(private readonly queryBus: QueryBus) {} + + @GrpcMethod('AuthorizationService', 'Decide') + async decide(data: DecisionRequestDto): Promise { + try { + const allow: boolean = await this.queryBus.execute( + new DecisionQuery(data.domain, data.action, data.context), + ); + return new DecisionResponseDto(allow); + } catch (error: any) { + throw new RpcException({ + code: RpcExceptionCode.PERMISSION_DENIED, + message: 'Permission denied', + }); + } + } +} diff --git a/src/modules/authorization/interface/grpc-controllers/dtos/decision.request.dto.ts b/src/modules/authorization/interface/grpc-controllers/dtos/decision.request.dto.ts new file mode 100644 index 0000000..13a60f2 --- /dev/null +++ b/src/modules/authorization/interface/grpc-controllers/dtos/decision.request.dto.ts @@ -0,0 +1,19 @@ +import { + Action, + ContextItem, + Domain, +} from '@modules/authorization/core/domain/authorization.types'; +import { IsArray, IsEnum, IsNotEmpty } from 'class-validator'; + +export class DecisionRequestDto { + @IsEnum(Domain) + @IsNotEmpty() + domain: Domain; + + @IsEnum(Action) + @IsNotEmpty() + action: Action; + + @IsArray() + context?: ContextItem[]; +} diff --git a/src/modules/authorization/mappers/authorization.profile.ts b/src/modules/authorization/mappers/authorization.profile.ts deleted file mode 100644 index db4419d..0000000 --- a/src/modules/authorization/mappers/authorization.profile.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createMap, Mapper } from '@automapper/core'; -import { AutomapperProfile, InjectMapper } from '@automapper/nestjs'; -import { Injectable } from '@nestjs/common'; -import { AuthorizationPresenter } from '../adapters/primaries/authorization.presenter'; -import { Authorization } from '../domain/entities/authorization'; - -@Injectable() -export class AuthorizationProfile extends AutomapperProfile { - constructor(@InjectMapper() mapper: Mapper) { - super(mapper); - } - - override get profile() { - return (mapper: any) => { - createMap(mapper, Authorization, AuthorizationPresenter); - }; - } -} diff --git a/src/modules/authorization/queries/decision.query.ts b/src/modules/authorization/queries/decision.query.ts deleted file mode 100644 index 16a5875..0000000 --- a/src/modules/authorization/queries/decision.query.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ContextItem } from '../domain/dtos/context-item'; -import { Action } from '../domain/dtos/action.enum'; -import { Domain } from '../domain/dtos/domain.enum'; - -export class DecisionQuery { - readonly domain: Domain; - readonly action: Action; - readonly context: Array; - - constructor(domain: Domain, action: Action, context?: Array) { - this.domain = domain; - this.action = action; - this.context = context; - } -} diff --git a/src/modules/authorization/tests/unit/core/decision.query-handler.spec.ts b/src/modules/authorization/tests/unit/core/decision.query-handler.spec.ts new file mode 100644 index 0000000..c0c7bbc --- /dev/null +++ b/src/modules/authorization/tests/unit/core/decision.query-handler.spec.ts @@ -0,0 +1,74 @@ +import { DECISION_MAKER } from '@modules/authorization/authorization.di-tokens'; +import { DecisionQuery } from '@modules/authorization/core/application/queries/decision/decision.query'; +import { DecisionQueryHandler } from '@modules/authorization/core/application/queries/decision/decision.query-handler'; +import { + Action, + Domain, +} from '@modules/authorization/core/domain/authorization.types'; + +import { Test, TestingModule } from '@nestjs/testing'; + +const mockDecisionMaker = { + decide: jest + .fn() + .mockImplementationOnce(() => true) + .mockImplementationOnce(() => false), +}; + +describe('Decision Query Handler', () => { + let decisionQueryHandler: DecisionQueryHandler; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: DECISION_MAKER, + useValue: mockDecisionMaker, + }, + DecisionQueryHandler, + ], + }).compile(); + + decisionQueryHandler = + module.get(DecisionQueryHandler); + }); + + it('should be defined', () => { + expect(decisionQueryHandler).toBeDefined(); + }); + + describe('execution', () => { + it('should return a positive decision', async () => { + const decisionQuery = new DecisionQuery(Domain.USER, Action.READ, [ + { + name: 'owner', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + { + name: 'id', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + ]); + const decision: boolean = await decisionQueryHandler.execute( + decisionQuery, + ); + expect(decision).toBeTruthy(); + }); + it('should return a negative decision', async () => { + const decisionQuery = new DecisionQuery(Domain.USER, Action.READ, [ + { + name: 'owner', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + { + name: 'id', + value: '96d99d44-e0a6-458e-a656-de2a400d60a9', + }, + ]); + const decision: boolean = await decisionQueryHandler.execute( + decisionQuery, + ); + expect(decision).toBeFalsy(); + }); + }); +}); diff --git a/src/modules/authorization/tests/unit/decision.usecase.spec.ts b/src/modules/authorization/tests/unit/decision.usecase.spec.ts deleted file mode 100644 index 1e2cc07..0000000 --- a/src/modules/authorization/tests/unit/decision.usecase.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker'; -import { Action } from '../../domain/dtos/action.enum'; -import { ContextItem } from '../../domain/dtos/context-item'; -import { DecisionRequest } from '../../domain/dtos/decision.request'; -import { Domain } from '../../domain/dtos/domain.enum'; -import { DecisionUseCase } from '../../domain/usecases/decision.usecase'; -import { AuthorizationProfile } from '../../mappers/authorization.profile'; -import { DecisionQuery } from '../../queries/decision.query'; - -const mockOpaDecisionMaker = { - decide: jest.fn().mockResolvedValue(Promise.resolve(true)), -}; - -describe('DecisionUseCase', () => { - let decisionUseCase: DecisionUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: OpaDecisionMaker, - useValue: mockOpaDecisionMaker, - }, - DecisionUseCase, - AuthorizationProfile, - ], - }).compile(); - - decisionUseCase = module.get(DecisionUseCase); - }); - - it('should be defined', () => { - expect(decisionUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should validate an authorization', async () => { - const decisionRequest: DecisionRequest = new DecisionRequest(); - decisionRequest.domain = Domain.USER; - decisionRequest.action = Action.CREATE; - decisionRequest.context = [new ContextItem('context1', 'value1')]; - expect( - decisionUseCase.execute( - new DecisionQuery( - decisionRequest.domain, - decisionRequest.action, - decisionRequest.context, - ), - ), - ).toBeTruthy(); - }); - }); -}); diff --git a/src/modules/authorization/tests/unit/infrastructure/opa.decision-maker.spec.ts b/src/modules/authorization/tests/unit/infrastructure/opa.decision-maker.spec.ts new file mode 100644 index 0000000..811d6c9 --- /dev/null +++ b/src/modules/authorization/tests/unit/infrastructure/opa.decision-maker.spec.ts @@ -0,0 +1,123 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigService } from '@nestjs/config'; +import { HttpService } from '@nestjs/axios'; +import { OpaDecisionMaker } from '@modules/authorization/infrastructure/opa.decision-maker'; +import { of } from 'rxjs'; +import { + Action, + Domain, +} from '@modules/authorization/core/domain/authorization.types'; + +const mockConfigService = { + get: jest.fn().mockImplementation(() => 'http://localhost:8181/v1/data/'), +}; +const mockHttpService = { + post: jest + .fn() + .mockImplementationOnce(() => { + return of({ + status: 200, + data: { + decision_id: 'b22965f0-f48a-4fcf-84db-b6f31cd07b8c', + result: { + allow: true, + }, + }, + }); + }) + .mockImplementationOnce(() => { + return of({ + status: 200, + data: { + decision_id: '5a648ea2-6790-4337-b63c-2ebdf225466f', + result: { + allow: false, + }, + }, + }); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('OPA decision maker', () => { + let opaDecisonMaker: OpaDecisionMaker; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: ConfigService, + useValue: mockConfigService, + }, + { + provide: HttpService, + useValue: mockHttpService, + }, + OpaDecisionMaker, + ], + }).compile(); + + opaDecisonMaker = module.get(OpaDecisionMaker); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(opaDecisonMaker).toBeDefined(); + }); + it('should return a positive decision', async () => { + const decision: boolean = await opaDecisonMaker.decide( + Domain.USER, + Action.READ, + [ + { + name: 'owner', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + { + name: 'id', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + ], + ); + expect(decision).toBeTruthy(); + }); + it('should return a negative decision', async () => { + const decision: boolean = await opaDecisonMaker.decide( + Domain.USER, + Action.READ, + [ + { + name: 'owner', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + { + name: 'id', + value: '96d99d44-e0a6-458e-a656-de2a400d60a9', + }, + ], + ); + expect(decision).toBeFalsy(); + }); + it('should return a negative decision if an error occurs', async () => { + const decision: boolean = await opaDecisonMaker.decide( + Domain.USER, + Action.READ, + [ + { + name: 'owner', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + { + name: 'id', + value: '96d99d44-e0a6-458e-a656-de2a400d60a9', + }, + ], + ); + expect(decision).toBeFalsy(); + }); +}); diff --git a/src/modules/authorization/tests/unit/interface/decide.grpc.controller.spec.ts b/src/modules/authorization/tests/unit/interface/decide.grpc.controller.spec.ts new file mode 100644 index 0000000..ac64327 --- /dev/null +++ b/src/modules/authorization/tests/unit/interface/decide.grpc.controller.spec.ts @@ -0,0 +1,91 @@ +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { + Action, + Domain, +} from '@modules/authorization/core/domain/authorization.types'; +import { DecisionResponseDto } from '@modules/authorization/interface/dtos/decision.response.dto'; +import { DecideGrpcController } from '@modules/authorization/interface/grpc-controllers/decide.grpc.controller'; +import { DecisionRequestDto } from '@modules/authorization/interface/grpc-controllers/dtos/decision.request.dto'; +import { QueryBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const decisionRequest: DecisionRequestDto = { + domain: Domain.USER, + action: Action.READ, + context: [ + { + name: 'owner', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + { + name: 'id', + value: '96d99d44-e0a6-458e-a656-de2a400d60a8', + }, + ], +}; + +const mockQueryBus = { + execute: jest + .fn() + .mockImplementationOnce(() => true) + .mockImplementationOnce(() => false) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Decide Grpc Controller', () => { + let decideGrpcController: DecideGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: QueryBus, + useValue: mockQueryBus, + }, + DecideGrpcController, + ], + }).compile(); + + decideGrpcController = + module.get(DecideGrpcController); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(decideGrpcController).toBeDefined(); + }); + + it('should return a positive decision response', async () => { + jest.spyOn(mockQueryBus, 'execute'); + const decisionResponse: DecisionResponseDto = + await decideGrpcController.decide(decisionRequest); + expect(decisionResponse.allow).toBeTruthy(); + expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should return a negative decision response', async () => { + jest.spyOn(mockQueryBus, 'execute'); + const decisionResponse: DecisionResponseDto = + await decideGrpcController.decide(decisionRequest); + expect(decisionResponse.allow).toBeFalsy(); + expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if authorization fails', async () => { + jest.spyOn(mockQueryBus, 'execute'); + expect.assertions(3); + try { + await decideGrpcController.decide(decisionRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.PERMISSION_DENIED); + } + expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authorization/tests/unit/opa.decision-maker.spec.ts b/src/modules/authorization/tests/unit/opa.decision-maker.spec.ts deleted file mode 100644 index 4874ae9..0000000 --- a/src/modules/authorization/tests/unit/opa.decision-maker.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { HttpService } from '@nestjs/axios'; -import { ConfigService } from '@nestjs/config'; -import { Test, TestingModule } from '@nestjs/testing'; -import { of } from 'rxjs'; -import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker'; -import { Action } from '../../domain/dtos/action.enum'; -import { Domain } from '../../domain/dtos/domain.enum'; - -const mockHttpService = { - post: jest - .fn() - .mockImplementationOnce(() => { - return of({ - status: 200, - data: { - decision_id: '96d99d44-e0a6-458e-a656-de2a400d60a8', - result: { - allow: true, - }, - }, - }); - }) - .mockImplementationOnce(() => { - return of({ - status: 200, - data: { - decision_id: '96d99d44-e0a6-458e-a656-de2a400d60a9', - result: { - allow: false, - }, - }, - }); - }) - .mockImplementationOnce(() => { - throw new Error(); - }), -}; - -const mockConfigService = { - get: jest.fn().mockResolvedValue({ - OPA_URL: 'http://url/', - }), -}; - -describe('OpaDecisionMaker', () => { - let opaDecisionMaker: OpaDecisionMaker; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [], - providers: [ - { - provide: HttpService, - useValue: mockHttpService, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - OpaDecisionMaker, - ], - }).compile(); - - opaDecisionMaker = module.get(OpaDecisionMaker); - }); - - it('should be defined', () => { - expect(opaDecisionMaker).toBeDefined(); - }); - - describe('execute', () => { - it('should return a truthy authorization', async () => { - const authorization = await opaDecisionMaker.decide( - Domain.USER, - Action.READ, - [{ name: 'uuid', value: 'bb281075-1b98-4456-89d6-c643d3044a91' }], - ); - expect(authorization.allow).toBeTruthy(); - }); - it('should return a falsy authorization', async () => { - const authorization = await opaDecisionMaker.decide( - Domain.USER, - Action.READ, - [{ name: 'uuid', value: 'bb281075-1b98-4456-89d6-c643d3044a91' }], - ); - expect(authorization.allow).toBeFalsy(); - }); - it('should return a falsy authorization when an error happens', async () => { - const authorization = await opaDecisionMaker.decide( - Domain.USER, - Action.READ, - [{ name: 'uuid', value: 'bb281075-1b98-4456-89d6-c643d3044a91' }], - ); - expect(authorization.allow).toBeFalsy(); - }); - }); -}); diff --git a/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts b/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts deleted file mode 100644 index 02c5cf9..0000000 --- a/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Prisma } from '@prisma/client'; -import { DatabaseException } from '../../exceptions/database.exception'; -import { ICollection } from '../../interfaces/collection.interface'; -import { IRepository } from '../../interfaces/repository.interface'; -import { PrismaService } from './prisma-service'; - -/** - * Child classes MUST redefined _model property with appropriate model name - */ -@Injectable() -export abstract class PrismaRepository implements IRepository { - protected model: string; - - constructor(protected readonly prisma: PrismaService) {} - - findAll = async ( - page = 1, - perPage = 10, - where?: any, - include?: any, - ): Promise> => { - const [data, total] = await this.prisma.$transaction([ - this.prisma[this.model].findMany({ - where, - include, - skip: (page - 1) * perPage, - take: perPage, - }), - this.prisma[this.model].count({ - where, - }), - ]); - return Promise.resolve({ - data, - total, - }); - }; - - findOneByUuid = async (uuid: string): Promise => { - try { - const entity = await this.prisma[this.model].findUnique({ - where: { uuid }, - }); - - return entity; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - findOne = async (where: any, include?: any): Promise => { - try { - const entity = await this.prisma[this.model].findFirst({ - where: where, - include: include, - }); - - return entity; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - ); - } else { - throw new DatabaseException(); - } - } - }; - - // TODO : using any is not good, but needed for nested entities - // TODO : Refactor for good clean architecture ? - async create(entity: Partial | any, include?: any): Promise { - try { - const res = await this.prisma[this.model].create({ - data: entity, - include: include, - }); - - return res; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - } - - update = async (uuid: string, entity: Partial): Promise => { - try { - const updatedEntity = await this.prisma[this.model].update({ - where: { uuid }, - data: entity, - }); - return updatedEntity; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - updateWhere = async ( - where: any, - entity: Partial | any, - include?: any, - ): Promise => { - try { - const updatedEntity = await this.prisma[this.model].update({ - where: where, - data: entity, - include: include, - }); - - return updatedEntity; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - delete = async (uuid: string): Promise => { - try { - const entity = await this.prisma[this.model].delete({ - where: { uuid }, - }); - - return entity; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - deleteMany = async (where: any): Promise => { - try { - const entity = await this.prisma[this.model].deleteMany({ - where: where, - }); - - return entity; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - findAllByQuery = async ( - include: string[], - where: string[], - ): Promise> => { - const query = `SELECT ${include.join(',')} FROM ${ - this.model - } WHERE ${where.join(' AND ')}`; - const data: T[] = await this.prisma.$queryRawUnsafe(query); - return Promise.resolve({ - data, - total: data.length, - }); - }; - - createWithFields = async (fields: object): Promise => { - try { - const command = `INSERT INTO ${this.model} ("${Object.keys(fields).join( - '","', - )}") VALUES (${Object.values(fields).join(',')})`; - return await this.prisma.$executeRawUnsafe(command); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - updateWithFields = async (uuid: string, entity: object): Promise => { - entity['"updatedAt"'] = `to_timestamp(${Date.now()} / 1000.0)`; - const values = Object.keys(entity).map((key) => `${key} = ${entity[key]}`); - try { - const command = `UPDATE ${this.model} SET ${values.join( - ', ', - )} WHERE uuid = '${uuid}'`; - return await this.prisma.$executeRawUnsafe(command); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; - - healthCheck = async (): Promise => { - try { - await this.prisma.$queryRaw`SELECT 1`; - return true; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseException( - Prisma.PrismaClientKnownRequestError.name, - e.code, - e.message, - ); - } else { - throw new DatabaseException(); - } - } - }; -} diff --git a/src/modules/database/adapters/secondaries/prisma-service.ts b/src/modules/database/adapters/secondaries/prisma-service.ts deleted file mode 100644 index edf6532..0000000 --- a/src/modules/database/adapters/secondaries/prisma-service.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client'; - -@Injectable() -export class PrismaService extends PrismaClient implements OnModuleInit { - async onModuleInit() { - await this.$connect(); - } - - async enableShutdownHooks(app: INestApplication) { - this.$on('beforeExit', async () => { - await app.close(); - }); - } -} diff --git a/src/modules/database/database.module.ts b/src/modules/database/database.module.ts deleted file mode 100644 index 40d3fc7..0000000 --- a/src/modules/database/database.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository'; -import { UsernameRepository } from '../oldauthentication/adapters/secondaries/username.repository'; -import { PrismaService } from './adapters/secondaries/prisma-service'; - -@Module({ - providers: [PrismaService, AuthenticationRepository, UsernameRepository], - exports: [PrismaService, AuthenticationRepository, UsernameRepository], -}) -export class DatabaseModule {} diff --git a/src/modules/database/domain/auth-repository.ts b/src/modules/database/domain/auth-repository.ts deleted file mode 100644 index e20e282..0000000 --- a/src/modules/database/domain/auth-repository.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { PrismaRepository } from '../adapters/secondaries/prisma-repository.abstract'; - -export class AuthRepository extends PrismaRepository {} diff --git a/src/modules/database/exceptions/database.exception.ts b/src/modules/database/exceptions/database.exception.ts deleted file mode 100644 index b0782a6..0000000 --- a/src/modules/database/exceptions/database.exception.ts +++ /dev/null @@ -1,24 +0,0 @@ -export class DatabaseException implements Error { - name: string; - message: string; - - constructor( - private _type: string = 'unknown', - private _code: string = '', - message?: string, - ) { - this.name = 'DatabaseException'; - this.message = message ?? 'An error occured with the database.'; - if (this.message.includes('Unique constraint failed')) { - this.message = 'Already exists.'; - } - } - - get type(): string { - return this._type; - } - - get code(): string { - return this._code; - } -} diff --git a/src/modules/database/interfaces/collection.interface.ts b/src/modules/database/interfaces/collection.interface.ts deleted file mode 100644 index 6e9a96d..0000000 --- a/src/modules/database/interfaces/collection.interface.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ICollection { - data: T[]; - total: number; -} diff --git a/src/modules/database/interfaces/repository.interface.ts b/src/modules/database/interfaces/repository.interface.ts deleted file mode 100644 index 8912545..0000000 --- a/src/modules/database/interfaces/repository.interface.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ICollection } from './collection.interface'; - -export interface IRepository { - findAll( - page: number, - perPage: number, - params?: any, - include?: any, - ): Promise>; - findOne(where: any, include?: any): Promise; - findOneByUuid(uuid: string, include?: any): Promise; - create(entity: Partial | any, include?: any): Promise; - update(uuid: string, entity: Partial, include?: any): Promise; - updateWhere(where: any, entity: Partial | any, include?: any): Promise; - delete(uuid: string): Promise; - deleteMany(where: any): Promise; -} diff --git a/src/modules/database/tests/unit/prisma-repository.spec.ts b/src/modules/database/tests/unit/prisma-repository.spec.ts deleted file mode 100644 index eb3bad0..0000000 --- a/src/modules/database/tests/unit/prisma-repository.spec.ts +++ /dev/null @@ -1,571 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Test, TestingModule } from '@nestjs/testing'; -import { PrismaService } from '../../adapters/secondaries/prisma-service'; -import { PrismaRepository } from '../../adapters/secondaries/prisma-repository.abstract'; -import { DatabaseException } from '../../exceptions/database.exception'; -import { Prisma } from '@prisma/client'; - -class FakeEntity { - uuid?: string; - name: string; -} - -let entityId = 2; -const entityUuid = 'uuid-'; -const entityName = 'name-'; - -const createRandomEntity = (): FakeEntity => { - const entity: FakeEntity = { - uuid: `${entityUuid}${entityId}`, - name: `${entityName}${entityId}`, - }; - - entityId++; - - return entity; -}; - -const fakeEntityToCreate: FakeEntity = { - name: 'test', -}; - -const fakeEntityCreated: FakeEntity = { - ...fakeEntityToCreate, - uuid: 'some-uuid', -}; - -const fakeEntities: FakeEntity[] = []; -Array.from({ length: 10 }).forEach(() => { - fakeEntities.push(createRandomEntity()); -}); - -@Injectable() -class FakePrismaRepository extends PrismaRepository { - protected model = 'fake'; -} - -class FakePrismaService extends PrismaService { - fake: any; -} - -const mockPrismaService = { - $transaction: jest.fn().mockImplementation(async (data: any) => { - const entities = await data[0]; - if (entities.length == 1) { - return Promise.resolve([[fakeEntityCreated], 1]); - } - - return Promise.resolve([fakeEntities, fakeEntities.length]); - }), - // eslint-disable-next-line @typescript-eslint/no-unused-vars - $queryRawUnsafe: jest.fn().mockImplementation((query?: string) => { - return Promise.resolve(fakeEntities); - }), - $executeRawUnsafe: jest - .fn() - .mockResolvedValueOnce(fakeEntityCreated) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((fields: object) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((fields: object) => { - throw new Error('an unknown error'); - }) - .mockResolvedValueOnce(fakeEntityCreated) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((fields: object) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((fields: object) => { - throw new Error('an unknown error'); - }), - $queryRaw: jest - .fn() - .mockImplementationOnce(() => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - .mockImplementationOnce(() => { - return true; - }) - .mockImplementation(() => { - throw new Prisma.PrismaClientKnownRequestError('Database unavailable', { - code: 'code', - clientVersion: 'version', - }); - }), - fake: { - create: jest - .fn() - .mockResolvedValueOnce(fakeEntityCreated) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Error('an unknown error'); - }), - - findMany: jest.fn().mockImplementation((params?: any) => { - if (params?.where?.limit == 1) { - return Promise.resolve([fakeEntityCreated]); - } - - return Promise.resolve(fakeEntities); - }), - count: jest.fn().mockResolvedValue(fakeEntities.length), - - findUnique: jest.fn().mockImplementation(async (params?: any) => { - let entity; - - if (params?.where?.uuid) { - entity = fakeEntities.find( - (entity) => entity.uuid === params?.where?.uuid, - ); - } - - if (!entity && params?.where?.uuid == 'unknown') { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - } else if (!entity) { - throw new Error('no entity'); - } - - return entity; - }), - - findFirst: jest - .fn() - .mockImplementationOnce((params?: any) => { - if (params?.where?.name) { - return Promise.resolve( - fakeEntities.find((entity) => entity.name === params?.where?.name), - ); - } - }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Error('an unknown error'); - }), - - update: jest - .fn() - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - .mockImplementationOnce((params: any) => { - const entity = fakeEntities.find( - (entity) => entity.name === params.where.name, - ); - Object.entries(params.data).map(([key, value]) => { - entity[key] = value; - }); - - return Promise.resolve(entity); - }) - .mockImplementation((params: any) => { - const entity = fakeEntities.find( - (entity) => entity.uuid === params.where.uuid, - ); - Object.entries(params.data).map(([key, value]) => { - entity[key] = value; - }); - - return Promise.resolve(entity); - }), - - delete: jest - .fn() - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - .mockImplementation((params: any) => { - let found = false; - - fakeEntities.forEach((entity, index) => { - if (entity.uuid === params?.where?.uuid) { - found = true; - fakeEntities.splice(index, 1); - } - }); - - if (!found) { - throw new Error(); - } - }), - - deleteMany: jest - .fn() - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementationOnce((params?: any) => { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - }) - .mockImplementation((params: any) => { - let found = false; - - fakeEntities.forEach((entity, index) => { - if (entity.uuid === params?.where?.uuid) { - found = true; - fakeEntities.splice(index, 1); - } - }); - - if (!found) { - throw new Error(); - } - }), - }, -}; - -describe('PrismaRepository', () => { - let fakeRepository: FakePrismaRepository; - let prisma: FakePrismaService; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - FakePrismaRepository, - { - provide: PrismaService, - useValue: mockPrismaService, - }, - ], - }).compile(); - - fakeRepository = module.get(FakePrismaRepository); - prisma = module.get(PrismaService) as FakePrismaService; - }); - - it('should be defined', () => { - expect(fakeRepository).toBeDefined(); - expect(prisma).toBeDefined(); - }); - - describe('findAll', () => { - it('should return an array of entities', async () => { - jest.spyOn(prisma.fake, 'findMany'); - jest.spyOn(prisma.fake, 'count'); - jest.spyOn(prisma, '$transaction'); - - const entities = await fakeRepository.findAll(); - expect(entities).toStrictEqual({ - data: fakeEntities, - total: fakeEntities.length, - }); - }); - - it('should return an array containing only one entity', async () => { - const entities = await fakeRepository.findAll(1, 10, { limit: 1 }); - - expect(prisma.fake.findMany).toHaveBeenCalledWith({ - skip: 0, - take: 10, - where: { limit: 1 }, - }); - expect(entities).toEqual({ - data: [fakeEntityCreated], - total: 1, - }); - }); - }); - - describe('create', () => { - it('should create an entity', async () => { - jest.spyOn(prisma.fake, 'create'); - - const newEntity = await fakeRepository.create(fakeEntityToCreate); - expect(newEntity).toBe(fakeEntityCreated); - expect(prisma.fake.create).toHaveBeenCalledTimes(1); - }); - - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.create(fakeEntityToCreate), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should throw a DatabaseException if uuid is not found', async () => { - await expect( - fakeRepository.create(fakeEntityToCreate), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('findOneByUuid', () => { - it('should find an entity by uuid', async () => { - const entity = await fakeRepository.findOneByUuid(fakeEntities[0].uuid); - expect(entity).toBe(fakeEntities[0]); - }); - - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.findOneByUuid('unknown'), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should throw a DatabaseException if uuid is not found', async () => { - await expect( - fakeRepository.findOneByUuid('wrong-uuid'), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('findOne', () => { - it('should find one entity', async () => { - const entity = await fakeRepository.findOne({ - name: fakeEntities[0].name, - }); - - expect(entity.name).toBe(fakeEntities[0].name); - }); - - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.findOne({ - name: fakeEntities[0].name, - }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should throw a DatabaseException for unknown error', async () => { - await expect( - fakeRepository.findOne({ - name: fakeEntities[0].name, - }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('update', () => { - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.update('fake-uuid', { name: 'error' }), - ).rejects.toBeInstanceOf(DatabaseException); - await expect( - fakeRepository.updateWhere({ name: 'error' }, { name: 'new error' }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should update an entity with name', async () => { - const newName = 'new-random-name'; - - await fakeRepository.updateWhere( - { name: fakeEntities[0].name }, - { - name: newName, - }, - ); - expect(fakeEntities[0].name).toBe(newName); - }); - - it('should update an entity with uuid', async () => { - const newName = 'random-name'; - - await fakeRepository.update(fakeEntities[0].uuid, { - name: newName, - }); - expect(fakeEntities[0].name).toBe(newName); - }); - - it("should throw an exception if an entity doesn't exist", async () => { - await expect( - fakeRepository.update('fake-uuid', { name: 'error' }), - ).rejects.toBeInstanceOf(DatabaseException); - await expect( - fakeRepository.updateWhere({ name: 'error' }, { name: 'new error' }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('delete', () => { - it('should throw a DatabaseException for client error', async () => { - await expect(fakeRepository.delete('fake-uuid')).rejects.toBeInstanceOf( - DatabaseException, - ); - }); - - it('should delete an entity', async () => { - const savedUuid = fakeEntities[0].uuid; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const res = await fakeRepository.delete(savedUuid); - - const deletedEntity = fakeEntities.find( - (entity) => entity.uuid === savedUuid, - ); - expect(deletedEntity).toBeUndefined(); - }); - - it("should throw an exception if an entity doesn't exist", async () => { - await expect(fakeRepository.delete('fake-uuid')).rejects.toBeInstanceOf( - DatabaseException, - ); - }); - }); - - describe('deleteMany', () => { - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.deleteMany({ uuid: 'fake-uuid' }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should delete entities based on their uuid', async () => { - const savedUuid = fakeEntities[0].uuid; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const res = await fakeRepository.deleteMany({ uuid: savedUuid }); - - const deletedEntity = fakeEntities.find( - (entity) => entity.uuid === savedUuid, - ); - expect(deletedEntity).toBeUndefined(); - }); - - it("should throw an exception if an entity doesn't exist", async () => { - await expect( - fakeRepository.deleteMany({ uuid: 'fake-uuid' }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('findAllByquery', () => { - it('should return an array of entities', async () => { - const entities = await fakeRepository.findAllByQuery( - ['uuid', 'name'], - ['name is not null'], - ); - expect(entities).toStrictEqual({ - data: fakeEntities, - total: fakeEntities.length, - }); - }); - }); - - describe('createWithFields', () => { - it('should create an entity', async () => { - jest.spyOn(prisma, '$queryRawUnsafe'); - - const newEntity = await fakeRepository.createWithFields({ - uuid: '804319b3-a09b-4491-9f82-7976bfce0aff', - name: 'my-name', - }); - expect(newEntity).toBe(fakeEntityCreated); - expect(prisma.$queryRawUnsafe).toHaveBeenCalledTimes(1); - }); - - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.createWithFields({ - uuid: '804319b3-a09b-4491-9f82-7976bfce0aff', - name: 'my-name', - }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should throw a DatabaseException if uuid is not found', async () => { - await expect( - fakeRepository.createWithFields({ - name: 'my-name', - }), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('updateWithFields', () => { - it('should update an entity', async () => { - jest.spyOn(prisma, '$queryRawUnsafe'); - - const updatedEntity = await fakeRepository.updateWithFields( - '804319b3-a09b-4491-9f82-7976bfce0aff', - { - name: 'my-name', - }, - ); - expect(updatedEntity).toBe(fakeEntityCreated); - expect(prisma.$queryRawUnsafe).toHaveBeenCalledTimes(1); - }); - - it('should throw a DatabaseException for client error', async () => { - await expect( - fakeRepository.updateWithFields( - '804319b3-a09b-4491-9f82-7976bfce0aff', - { - name: 'my-name', - }, - ), - ).rejects.toBeInstanceOf(DatabaseException); - }); - - it('should throw a DatabaseException if uuid is not found', async () => { - await expect( - fakeRepository.updateWithFields( - '804319b3-a09b-4491-9f82-7976bfce0aff', - { - name: 'my-name', - }, - ), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('healthCheck', () => { - it('should throw a DatabaseException for client error', async () => { - await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf( - DatabaseException, - ); - }); - - it('should return a healthy result', async () => { - const res = await fakeRepository.healthCheck(); - expect(res).toBeTruthy(); - }); - - it('should throw an exception if database is not available', async () => { - await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf( - DatabaseException, - ); - }); - }); -}); diff --git a/src/modules/health/adapters/primaries/health.controller.ts b/src/modules/health/adapters/primaries/health.controller.ts deleted file mode 100644 index caf3cf3..0000000 --- a/src/modules/health/adapters/primaries/health.controller.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { - HealthCheckService, - HealthCheck, - HealthCheckResult, -} from '@nestjs/terminus'; -import { Messager } from '../secondaries/messager'; -import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; - -// this controller responds to rest GET /health -@Controller('health') -export class HealthController { - constructor( - private readonly prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase, - private healthCheckService: HealthCheckService, - private messager: Messager, - ) {} - - @Get() - @HealthCheck() - async check() { - try { - return await this.healthCheckService.check([ - async () => this.prismaHealthIndicatorUseCase.isHealthy('prisma'), - ]); - } catch (error) { - const healthCheckResult: HealthCheckResult = error.response; - this.messager.publish( - 'logging.auth.health.crit', - JSON.stringify(healthCheckResult.error), - ); - throw error; - } - } -} diff --git a/src/modules/health/adapters/secondaries/message-broker.ts b/src/modules/health/adapters/secondaries/message-broker.ts deleted file mode 100644 index 594aa43..0000000 --- a/src/modules/health/adapters/secondaries/message-broker.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export abstract class IMessageBroker { - exchange: string; - - constructor(exchange: string) { - this.exchange = exchange; - } - - abstract publish(routingKey: string, message: string): void; -} diff --git a/src/modules/health/adapters/secondaries/messager.ts b/src/modules/health/adapters/secondaries/messager.ts deleted file mode 100644 index cd7e7ef..0000000 --- a/src/modules/health/adapters/secondaries/messager.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { IMessageBroker } from './message-broker'; - -@Injectable() -export class Messager extends IMessageBroker { - constructor( - private readonly amqpConnection: AmqpConnection, - configService: ConfigService, - ) { - super(configService.get('RMQ_EXCHANGE')); - } - - publish = (routingKey: string, message: string): void => { - this.amqpConnection.publish(this.exchange, routingKey, message); - }; -} diff --git a/src/modules/health/core/application/ports/check-repository.port.ts b/src/modules/health/core/application/ports/check-repository.port.ts new file mode 100644 index 0000000..64d8980 --- /dev/null +++ b/src/modules/health/core/application/ports/check-repository.port.ts @@ -0,0 +1,3 @@ +export interface CheckRepositoryPort { + healthCheck(): Promise; +} diff --git a/src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts b/src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts new file mode 100644 index 0000000..300af4b --- /dev/null +++ b/src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts @@ -0,0 +1,54 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { + HealthCheckError, + HealthCheckResult, + HealthIndicator, + HealthIndicatorResult, +} from '@nestjs/terminus'; +import { CheckRepositoryPort } from '../ports/check-repository.port'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/health/health.di-tokens'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { LOGGING_AUTHENTICATION_HEALTH_CRIT } from '@modules/health/health.constants'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; +import { AuthenticationRepositoryPort } from '@modules/authentication/core/application/ports/authentication.repository.port'; +import { UsernameRepositoryPort } from '@modules/authentication/core/application/ports/username.repository.port'; + +@Injectable() +export class RepositoriesHealthIndicatorUseCase extends HealthIndicator { + private _checkRepositories: CheckRepositoryPort[]; + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly authenticationRepository: AuthenticationRepositoryPort, + @Inject(USERNAME_REPOSITORY) + private readonly usernameRepository: UsernameRepositoryPort, + @Inject(MESSAGE_PUBLISHER) + private readonly messagePublisher: MessagePublisherPort, + ) { + super(); + this._checkRepositories = [authenticationRepository, usernameRepository]; + } + isHealthy = async (key: string): Promise => { + try { + await Promise.all( + this._checkRepositories.map( + async (checkRepository: CheckRepositoryPort) => { + await checkRepository.healthCheck(); + }, + ), + ); + return this.getStatus(key, true); + } catch (error) { + const healthCheckResult: HealthCheckResult = error; + this.messagePublisher.publish( + LOGGING_AUTHENTICATION_HEALTH_CRIT, + JSON.stringify(healthCheckResult.error), + ); + throw new HealthCheckError('Repository', { + repository: error.message, + }); + } + }; +} diff --git a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts b/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts deleted file mode 100644 index 2d77a7b..0000000 --- a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - HealthCheckError, - HealthIndicator, - HealthIndicatorResult, -} from '@nestjs/terminus'; -import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository'; - -@Injectable() -export class PrismaHealthIndicatorUseCase extends HealthIndicator { - constructor(private readonly repository: AuthenticationRepository) { - super(); - } - - isHealthy = async (key: string): Promise => { - try { - await this.repository.healthCheck(); - return this.getStatus(key, true); - } catch (e) { - throw new HealthCheckError('Prisma', { - prisma: e.message, - }); - } - }; -} diff --git a/src/modules/health/health.constants.ts b/src/modules/health/health.constants.ts new file mode 100644 index 0000000..53384ab --- /dev/null +++ b/src/modules/health/health.constants.ts @@ -0,0 +1 @@ +export const LOGGING_AUTHENTICATION_HEALTH_CRIT = 'logging.auth.health.crit'; diff --git a/src/modules/health/health.di-tokens.ts b/src/modules/health/health.di-tokens.ts new file mode 100644 index 0000000..5160269 --- /dev/null +++ b/src/modules/health/health.di-tokens.ts @@ -0,0 +1,2 @@ +export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY'); +export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY'); diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index 374fe2f..6d8706f 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -1,34 +1,45 @@ -import { Module } from '@nestjs/common'; -import { HealthServerController } from './adapters/primaries/health-server.controller'; -import { PrismaHealthIndicatorUseCase } from './domain/usecases/prisma.health-indicator.usecase'; -import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository'; -import { DatabaseModule } from '../database/database.module'; -import { HealthController } from './adapters/primaries/health.controller'; +import { Module, Provider } from '@nestjs/common'; +import { HealthHttpController } from './interface/http-controllers/health.http.controller'; import { TerminusModule } from '@nestjs/terminus'; -import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { Messager } from './adapters/secondaries/messager'; +import { RepositoriesHealthIndicatorUseCase } from './core/application/usecases/repositories.health-indicator.usecase'; +import { HealthGrpcController } from './interface/grpc-controllers/health.grpc.controller'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { AuthenticationRepository } from '@modules/authentication/infrastructure/authentication.repository'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from './health.di-tokens'; +import { UsernameRepository } from '@modules/authentication/infrastructure/username.repository'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; + +const grpcControllers = [HealthGrpcController]; + +const httpControllers = [HealthHttpController]; + +const useCases: Provider[] = [RepositoriesHealthIndicatorUseCase]; + +const repositories: Provider[] = [ + { + provide: AUTHENTICATION_REPOSITORY, + useClass: AuthenticationRepository, + }, + { + provide: USERNAME_REPOSITORY, + useClass: UsernameRepository, + }, +]; + +const messageBrokers: Provider[] = [ + { + provide: MESSAGE_PUBLISHER, + useClass: MessageBrokerPublisher, + }, +]; @Module({ - imports: [ - TerminusModule, - RabbitMQModule.forRootAsync(RabbitMQModule, { - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - exchanges: [ - { - name: configService.get('RMQ_EXCHANGE'), - type: 'topic', - }, - ], - uri: configService.get('RMQ_URI'), - connectionInitOptions: { wait: false }, - }), - inject: [ConfigService], - }), - DatabaseModule, - ], - controllers: [HealthServerController, HealthController], - providers: [PrismaHealthIndicatorUseCase, AuthenticationRepository, Messager], + imports: [TerminusModule, AuthenticationModule], + controllers: [...grpcControllers, ...httpControllers], + providers: [...useCases, ...repositories, ...messageBrokers], }) export class HealthModule {} diff --git a/src/modules/health/adapters/primaries/health-server.controller.ts b/src/modules/health/interface/grpc-controllers/health.grpc.controller.ts similarity index 56% rename from src/modules/health/adapters/primaries/health-server.controller.ts rename to src/modules/health/interface/grpc-controllers/health.grpc.controller.ts index 858f705..a016ee4 100644 --- a/src/modules/health/adapters/primaries/health-server.controller.ts +++ b/src/modules/health/interface/grpc-controllers/health.grpc.controller.ts @@ -1,8 +1,8 @@ import { Controller } from '@nestjs/common'; import { GrpcMethod } from '@nestjs/microservices'; -import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; +import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase'; -enum ServingStatus { +export enum ServingStatus { UNKNOWN = 0, SERVING = 1, NOT_SERVING = 2, @@ -16,26 +16,25 @@ interface HealthCheckResponse { status: ServingStatus; } -// this controller responds to gRPC health check service @Controller() -export class HealthServerController { +export class HealthGrpcController { constructor( - private readonly prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase, + private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase, ) {} @GrpcMethod('Health', 'Check') async check( // eslint-disable-next-line @typescript-eslint/no-unused-vars - data: HealthCheckRequest, + data?: HealthCheckRequest, // eslint-disable-next-line @typescript-eslint/no-unused-vars - metadata: any, + metadata?: any, ): Promise { - const healthCheck = await this.prismaHealthIndicatorUseCase.isHealthy( - 'prisma', + const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy( + 'repositories', ); return { status: - healthCheck['prisma'].status == 'up' + healthCheck['repositories'].status == 'up' ? ServingStatus.SERVING : ServingStatus.NOT_SERVING, }; diff --git a/src/modules/health/adapters/primaries/health.proto b/src/modules/health/interface/grpc-controllers/health.proto similarity index 100% rename from src/modules/health/adapters/primaries/health.proto rename to src/modules/health/interface/grpc-controllers/health.proto diff --git a/src/modules/health/interface/http-controllers/health.http.controller.ts b/src/modules/health/interface/http-controllers/health.http.controller.ts new file mode 100644 index 0000000..32428e9 --- /dev/null +++ b/src/modules/health/interface/http-controllers/health.http.controller.ts @@ -0,0 +1,28 @@ +import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase'; +import { Controller, Get } from '@nestjs/common'; +import { + HealthCheckService, + HealthCheck, + HealthCheckResult, +} from '@nestjs/terminus'; + +@Controller('health') +export class HealthHttpController { + constructor( + private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase, + private readonly healthCheckService: HealthCheckService, + ) {} + + @Get() + @HealthCheck() + async check(): Promise { + try { + return await this.healthCheckService.check([ + async () => + this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'), + ]); + } catch (error) { + throw error; + } + } +} diff --git a/src/modules/health/tests/unit/health.grpc.controller.spec.ts b/src/modules/health/tests/unit/health.grpc.controller.spec.ts new file mode 100644 index 0000000..25434ad --- /dev/null +++ b/src/modules/health/tests/unit/health.grpc.controller.spec.ts @@ -0,0 +1,72 @@ +import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase'; +import { + HealthGrpcController, + ServingStatus, +} from '@modules/health/interface/grpc-controllers/health.grpc.controller'; +import { Test, TestingModule } from '@nestjs/testing'; + +const mockRepositoriesHealthIndicatorUseCase = { + isHealthy: jest + .fn() + .mockImplementationOnce(() => ({ + repositories: { + status: 'up', + }, + })) + .mockImplementationOnce(() => ({ + repositories: { + status: 'down', + }, + })), +}; + +describe('Health Grpc Controller', () => { + let healthGrpcController: HealthGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: RepositoriesHealthIndicatorUseCase, + useValue: mockRepositoriesHealthIndicatorUseCase, + }, + HealthGrpcController, + ], + }).compile(); + + healthGrpcController = + module.get(HealthGrpcController); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(healthGrpcController).toBeDefined(); + }); + + it('should return a Serving status ', async () => { + jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); + const servingStatus: { status: ServingStatus } = + await healthGrpcController.check(); + expect(servingStatus).toEqual({ + status: ServingStatus.SERVING, + }); + expect( + mockRepositoriesHealthIndicatorUseCase.isHealthy, + ).toHaveBeenCalledTimes(1); + }); + + it('should return a Not Serving status ', async () => { + jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); + const servingStatus: { status: ServingStatus } = + await healthGrpcController.check(); + expect(servingStatus).toEqual({ + status: ServingStatus.NOT_SERVING, + }); + expect( + mockRepositoriesHealthIndicatorUseCase.isHealthy, + ).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/health/tests/unit/health.http.controller.spec.ts b/src/modules/health/tests/unit/health.http.controller.spec.ts new file mode 100644 index 0000000..3fe40bb --- /dev/null +++ b/src/modules/health/tests/unit/health.http.controller.spec.ts @@ -0,0 +1,90 @@ +import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase'; +import { HealthHttpController } from '@modules/health/interface/http-controllers/health.http.controller'; +import { HealthCheckResult, HealthCheckService } from '@nestjs/terminus'; +import { Test, TestingModule } from '@nestjs/testing'; + +const mockHealthCheckService = { + check: jest + .fn() + .mockImplementationOnce(() => ({ + status: 'ok', + info: { + repositories: { + status: 'up', + }, + }, + error: {}, + details: { + repositories: { + status: 'up', + }, + }, + })) + .mockImplementationOnce(() => ({ + status: 'error', + info: {}, + error: { + repository: + "\nInvalid `prisma.$queryRaw()` invocation:\n\n\nCan't reach database server at `v3-db`:`5432`\n\nPlease make sure your database server is running at `v3-db`:`5432`.", + }, + details: { + repository: + "\nInvalid `prisma.$queryRaw()` invocation:\n\n\nCan't reach database server at `v3-db`:`5432`\n\nPlease make sure your database server is running at `v3-db`:`5432`.", + }, + })), +}; + +const mockRepositoriesHealthIndicatorUseCase = { + isHealthy: jest.fn(), +}; + +describe('Health Http Controller', () => { + let healthHttpController: HealthHttpController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: HealthCheckService, + useValue: mockHealthCheckService, + }, + { + provide: RepositoriesHealthIndicatorUseCase, + useValue: mockRepositoriesHealthIndicatorUseCase, + }, + HealthHttpController, + ], + }).compile(); + + healthHttpController = + module.get(HealthHttpController); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(healthHttpController).toBeDefined(); + }); + + it('should return an HealthCheckResult with Ok status ', async () => { + jest.spyOn(mockHealthCheckService, 'check'); + jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); + + const healthCheckResult: HealthCheckResult = + await healthHttpController.check(); + expect(healthCheckResult.status).toBe('ok'); + expect(mockHealthCheckService.check).toHaveBeenCalledTimes(1); + }); + + it('should return an HealthCheckResult with Error status ', async () => { + jest.spyOn(mockHealthCheckService, 'check'); + jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); + + const healthCheckResult: HealthCheckResult = + await healthHttpController.check(); + expect(healthCheckResult.status).toBe('error'); + expect(mockHealthCheckService.check).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/health/tests/unit/messager.spec.ts b/src/modules/health/tests/unit/messager.spec.ts deleted file mode 100644 index 0331332..0000000 --- a/src/modules/health/tests/unit/messager.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { ConfigService } from '@nestjs/config'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Messager } from '../../adapters/secondaries/messager'; - -const mockAmqpConnection = { - publish: jest.fn().mockImplementation(), -}; - -const mockConfigService = { - get: jest.fn().mockResolvedValue({ - RMQ_EXCHANGE: 'mobicoop', - }), -}; - -describe('Messager', () => { - let messager: Messager; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [], - providers: [ - Messager, - { - provide: AmqpConnection, - useValue: mockAmqpConnection, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - messager = module.get(Messager); - }); - - it('should be defined', () => { - expect(messager).toBeDefined(); - }); - - it('should publish a message', async () => { - jest.spyOn(mockAmqpConnection, 'publish'); - messager.publish('test.create.info', 'my-test'); - expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts deleted file mode 100644 index d7d9971..0000000 --- a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; -import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime'; -import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; - -const mockAuthenticationRepository = { - healthCheck: jest - .fn() - .mockImplementationOnce(() => { - return Promise.resolve(true); - }) - .mockImplementation(() => { - throw new PrismaClientKnownRequestError('Service unavailable', { - code: 'code', - clientVersion: 'version', - }); - }), -}; - -describe('PrismaHealthIndicatorUseCase', () => { - let prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: AuthenticationRepository, - useValue: mockAuthenticationRepository, - }, - PrismaHealthIndicatorUseCase, - ], - }).compile(); - - prismaHealthIndicatorUseCase = module.get( - PrismaHealthIndicatorUseCase, - ); - }); - - it('should be defined', () => { - expect(prismaHealthIndicatorUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should check health successfully', async () => { - const healthIndicatorResult: HealthIndicatorResult = - await prismaHealthIndicatorUseCase.isHealthy('prisma'); - - expect(healthIndicatorResult['prisma'].status).toBe('up'); - }); - - it('should throw an error if database is unavailable', async () => { - await expect( - prismaHealthIndicatorUseCase.isHealthy('prisma'), - ).rejects.toBeInstanceOf(HealthCheckError); - }); - }); -}); diff --git a/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts new file mode 100644 index 0000000..eab6723 --- /dev/null +++ b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts @@ -0,0 +1,84 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; +import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { DatabaseErrorException } from '@mobicoop/ddd-library'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/health/health.di-tokens'; + +const mockAuthenticationRepository = { + healthCheck: jest + .fn() + .mockImplementationOnce(() => { + return Promise.resolve(true); + }) + .mockImplementation(() => { + throw new DatabaseErrorException('An error occured in the database'); + }), +}; + +const mockUsernameRepository = { + healthCheck: jest + .fn() + .mockImplementationOnce(() => { + return Promise.resolve(true); + }) + .mockImplementation(() => { + throw new DatabaseErrorException('An error occured in the database'); + }), +}; + +const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), +}; + +describe('RepositoriesHealthIndicatorUseCase', () => { + let repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + RepositoriesHealthIndicatorUseCase, + { + provide: AUTHENTICATION_REPOSITORY, + useValue: mockAuthenticationRepository, + }, + { + provide: USERNAME_REPOSITORY, + useValue: mockUsernameRepository, + }, + { + provide: MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, + }, + ], + }).compile(); + + repositoriesHealthIndicatorUseCase = + module.get( + RepositoriesHealthIndicatorUseCase, + ); + }); + + it('should be defined', () => { + expect(repositoriesHealthIndicatorUseCase).toBeDefined(); + }); + + describe('execute', () => { + it('should check health successfully', async () => { + const healthIndicatorResult: HealthIndicatorResult = + await repositoriesHealthIndicatorUseCase.isHealthy('repositories'); + expect(healthIndicatorResult['repositories'].status).toBe('up'); + }); + + it('should throw an error if database is unavailable', async () => { + jest.spyOn(mockMessagePublisher, 'publish'); + await expect( + repositoriesHealthIndicatorUseCase.isHealthy('repositories'), + ).rejects.toBeInstanceOf(HealthCheckError); + expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts b/src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts deleted file mode 100644 index 1c0cba2..0000000 --- a/src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq'; -import { Controller } from '@nestjs/common'; -import { CommandBus } from '@nestjs/cqrs'; -import { UpdateUsernameCommand } from '../../commands/update-username.command'; -import { Type } from '../../domain/dtos/type.enum'; -import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request'; -import { DeleteAuthenticationRequest } from '../../domain/dtos/delete-authentication.request'; -import { DeleteAuthenticationCommand } from '../../commands/delete-authentication.command'; - -@Controller() -export class AuthenticationMessagerController { - constructor(private readonly commandBus: CommandBus) {} - - @RabbitSubscribe({ - name: 'userUpdate', - }) - public async userUpdatedHandler(message: string) { - const updatedUser = JSON.parse(message); - if (!updatedUser.hasOwnProperty('uuid')) throw new Error(); - if (updatedUser.hasOwnProperty('email') && updatedUser.email) { - const updateUsernameRequest = new UpdateUsernameRequest(); - updateUsernameRequest.uuid = updatedUser.uuid; - updateUsernameRequest.username = updatedUser.email; - updateUsernameRequest.type = Type.EMAIL; - await this.commandBus.execute( - new UpdateUsernameCommand(updateUsernameRequest), - ); - } - if (updatedUser.hasOwnProperty('phone') && updatedUser.phone) { - const updateUsernameRequest = new UpdateUsernameRequest(); - updateUsernameRequest.uuid = updatedUser.uuid; - updateUsernameRequest.username = updatedUser.phone; - updateUsernameRequest.type = Type.PHONE; - await this.commandBus.execute( - new UpdateUsernameCommand(updateUsernameRequest), - ); - } - } - - @RabbitSubscribe({ - name: 'userDelete', - }) - public async userDeletedHandler(message: string) { - const deletedUser = JSON.parse(message); - if (!deletedUser.hasOwnProperty('uuid')) throw new Error(); - const deleteAuthenticationRequest = new DeleteAuthenticationRequest(); - deleteAuthenticationRequest.uuid = deletedUser.uuid; - await this.commandBus.execute( - new DeleteAuthenticationCommand(deleteAuthenticationRequest), - ); - } -} diff --git a/src/modules/oldauthentication/adapters/primaries/authentication.controller.ts b/src/modules/oldauthentication/adapters/primaries/authentication.controller.ts deleted file mode 100644 index af72330..0000000 --- a/src/modules/oldauthentication/adapters/primaries/authentication.controller.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { Mapper } from '@automapper/core'; -import { InjectMapper } from '@automapper/nestjs'; -import { Controller, UsePipes } from '@nestjs/common'; -import { CommandBus, QueryBus } from '@nestjs/cqrs'; -import { GrpcMethod, RpcException } from '@nestjs/microservices'; -import { DatabaseException } from 'src/modules/database/exceptions/database.exception'; -import { AddUsernameCommand } from '../../commands/add-username.command'; -import { CreateAuthenticationCommand } from '../../commands/create-authentication.command'; -import { DeleteAuthenticationCommand } from '../../commands/delete-authentication.command'; -import { DeleteUsernameCommand } from '../../commands/delete-username.command'; -import { UpdatePasswordCommand } from '../../commands/update-password.command'; -import { UpdateUsernameCommand } from '../../commands/update-username.command'; -import { AddUsernameRequest } from '../../domain/dtos/add-username.request'; -import { CreateAuthenticationRequest } from '../../domain/dtos/create-authentication.request'; -import { DeleteAuthenticationRequest } from '../../domain/dtos/delete-authentication.request'; -import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request'; -import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request'; -import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request'; -import { ValidateAuthenticationRequest } from '../../domain/dtos/validate-authentication.request'; -import { Authentication } from '../../domain/entities/authentication'; -import { Username } from '../../domain/entities/username'; -import { ValidateAuthenticationQuery } from '../../queries/validate-authentication.query'; -import { AuthenticationPresenter } from './authentication.presenter'; -import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe'; -import { UsernamePresenter } from './username.presenter'; - -@UsePipes( - new RpcValidationPipe({ - whitelist: true, - forbidUnknownValues: false, - }), -) -@Controller() -export class AuthenticationController { - constructor( - private readonly commandBus: CommandBus, - private readonly queryBus: QueryBus, - @InjectMapper() private readonly mapper: Mapper, - ) {} - - @GrpcMethod('AuthenticationService', 'Validate') - async validate( - data: ValidateAuthenticationRequest, - ): Promise { - try { - const authentication: Authentication = await this.queryBus.execute( - new ValidateAuthenticationQuery(data.username, data.password), - ); - return this.mapper.map( - authentication, - Authentication, - AuthenticationPresenter, - ); - } catch (e) { - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } - - @GrpcMethod('AuthenticationService', 'Create') - async createUser( - data: CreateAuthenticationRequest, - ): Promise { - try { - const authentication: Authentication = await this.commandBus.execute( - new CreateAuthenticationCommand(data), - ); - return this.mapper.map( - authentication, - Authentication, - AuthenticationPresenter, - ); - } catch (e) { - if (e instanceof DatabaseException) { - if (e.message.includes('Already exists')) { - throw new RpcException({ - code: 6, - message: 'Auth already exists', - }); - } - } - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } - - @GrpcMethod('AuthenticationService', 'AddUsername') - async addUsername(data: AddUsernameRequest): Promise { - try { - const username: Username = await this.commandBus.execute( - new AddUsernameCommand(data), - ); - - return this.mapper.map(username, Username, UsernamePresenter); - } catch (e) { - if (e instanceof DatabaseException) { - if (e.message.includes('Already exists')) { - throw new RpcException({ - code: 6, - message: 'Username already exists', - }); - } - } - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } - - @GrpcMethod('AuthenticationService', 'UpdateUsername') - async updateUsername( - data: UpdateUsernameRequest, - ): Promise { - try { - const username: Username = await this.commandBus.execute( - new UpdateUsernameCommand(data), - ); - - return this.mapper.map(username, Username, UsernamePresenter); - } catch (e) { - if (e instanceof DatabaseException) { - if (e.message.includes('Already exists')) { - throw new RpcException({ - code: 6, - message: 'Username already exists', - }); - } - } - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } - - @GrpcMethod('AuthenticationService', 'UpdatePassword') - async updatePassword( - data: UpdatePasswordRequest, - ): Promise { - try { - const authentication: Authentication = await this.commandBus.execute( - new UpdatePasswordCommand(data), - ); - - return this.mapper.map( - authentication, - Authentication, - AuthenticationPresenter, - ); - } catch (e) { - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } - - @GrpcMethod('AuthenticationService', 'DeleteUsername') - async deleteUsername(data: DeleteUsernameRequest) { - try { - return await this.commandBus.execute(new DeleteUsernameCommand(data)); - } catch (e) { - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } - - @GrpcMethod('AuthenticationService', 'Delete') - async deleteAuthentication(data: DeleteAuthenticationRequest) { - try { - return await this.commandBus.execute( - new DeleteAuthenticationCommand(data), - ); - } catch (e) { - throw new RpcException({ - code: 7, - message: 'Permission denied', - }); - } - } -} diff --git a/src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts b/src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts deleted file mode 100644 index 080c7eb..0000000 --- a/src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { AutoMap } from '@automapper/classes'; - -export class AuthenticationPresenter { - @AutoMap() - uuid: string; -} diff --git a/src/modules/oldauthentication/adapters/primaries/authentication.proto b/src/modules/oldauthentication/adapters/primaries/authentication.proto deleted file mode 100644 index 9b16d3a..0000000 --- a/src/modules/oldauthentication/adapters/primaries/authentication.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; - -package authentication; - -service AuthenticationService { - rpc Validate(AuthenticationByUsernamePassword) returns (Uuid); - rpc Create(Authentication) returns (Uuid); - rpc AddUsername(Username) returns (Uuid); - rpc UpdatePassword(Password) returns (Uuid); - rpc UpdateUsername(Username) returns (Uuid); - rpc DeleteUsername(Username) returns (Uuid); - rpc Delete(Uuid) returns (Empty); -} - -message AuthenticationByUsernamePassword { - string username = 1; - string password = 2; -} - -message Authentication { - string uuid = 1; - string username = 2; - string password = 3; - string type = 4; -} - -message Password { - string uuid = 1; - string password = 2; -} - -message Username { - string uuid = 1; - string username = 2; - string type = 3; -} - -message Uuid { - string uuid = 1; -} - -message Empty {} diff --git a/src/modules/oldauthentication/adapters/primaries/username.presenter.ts b/src/modules/oldauthentication/adapters/primaries/username.presenter.ts deleted file mode 100644 index 167e25a..0000000 --- a/src/modules/oldauthentication/adapters/primaries/username.presenter.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AutoMap } from '@automapper/classes'; - -export class UsernamePresenter { - @AutoMap() - uuid: string; - - @AutoMap() - username: string; -} diff --git a/src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts b/src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts deleted file mode 100644 index e8fe067..0000000 --- a/src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { AuthRepository } from '../../../database/domain/auth-repository'; -import { Authentication } from '../../domain/entities/authentication'; - -@Injectable() -export class AuthenticationRepository extends AuthRepository { - protected model = 'auth'; -} diff --git a/src/modules/oldauthentication/adapters/secondaries/messager.ts b/src/modules/oldauthentication/adapters/secondaries/messager.ts deleted file mode 100644 index afbbcb4..0000000 --- a/src/modules/oldauthentication/adapters/secondaries/messager.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { IMessageBroker } from '../../domain/interfaces/message-broker'; - -@Injectable() -export class Messager extends IMessageBroker { - constructor( - private readonly amqpConnection: AmqpConnection, - configService: ConfigService, - ) { - super(configService.get('RMQ_EXCHANGE')); - } - - publish = (routingKey: string, message: string): void => { - this.amqpConnection.publish(this.exchange, routingKey, message); - }; -} diff --git a/src/modules/oldauthentication/adapters/secondaries/username.repository.ts b/src/modules/oldauthentication/adapters/secondaries/username.repository.ts deleted file mode 100644 index 53a5bbe..0000000 --- a/src/modules/oldauthentication/adapters/secondaries/username.repository.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { AuthRepository } from '../../../database/domain/auth-repository'; -import { Username } from '../../domain/entities/username'; - -@Injectable() -export class UsernameRepository extends AuthRepository { - protected model = 'username'; -} diff --git a/src/modules/oldauthentication/authentication.module.ts b/src/modules/oldauthentication/authentication.module.ts deleted file mode 100644 index 3b7ed79..0000000 --- a/src/modules/oldauthentication/authentication.module.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CqrsModule } from '@nestjs/cqrs'; -import { DatabaseModule } from '../database/database.module'; -import { AuthenticationController } from './adapters/primaries/authentication.controller'; -import { CreateAuthenticationUseCase } from './domain/usecases/create-authentication.usecase'; -import { ValidateAuthenticationUseCase } from './domain/usecases/validate-authentication.usecase'; -import { AuthenticationProfile } from './mappers/authentication.profile'; -import { AuthenticationRepository } from './adapters/secondaries/authentication.repository'; -import { UpdateUsernameUseCase } from './domain/usecases/update-username.usecase'; -import { UsernameProfile } from './mappers/username.profile'; -import { AddUsernameUseCase } from './domain/usecases/add-username.usecase'; -import { UpdatePasswordUseCase } from './domain/usecases/update-password.usecase'; -import { DeleteUsernameUseCase } from './domain/usecases/delete-username.usecase'; -import { DeleteAuthenticationUseCase } from './domain/usecases/delete-authentication.usecase'; -import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { AuthenticationMessagerController } from './adapters/primaries/authentication-messager.controller'; -import { Messager } from './adapters/secondaries/messager'; - -@Module({ - imports: [ - DatabaseModule, - CqrsModule, - RabbitMQModule.forRootAsync(RabbitMQModule, { - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - exchanges: [ - { - name: configService.get('RMQ_EXCHANGE'), - type: 'topic', - }, - ], - handlers: { - userUpdate: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'user.update', - }, - userDelete: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'user.delete', - }, - }, - uri: configService.get('RMQ_URI'), - connectionInitOptions: { wait: false }, - enableControllerDiscovery: true, - }), - inject: [ConfigService], - }), - ], - controllers: [AuthenticationController, AuthenticationMessagerController], - providers: [ - AuthenticationProfile, - UsernameProfile, - AuthenticationRepository, - Messager, - ValidateAuthenticationUseCase, - CreateAuthenticationUseCase, - AddUsernameUseCase, - UpdateUsernameUseCase, - UpdatePasswordUseCase, - DeleteUsernameUseCase, - DeleteAuthenticationUseCase, - ], - exports: [], -}) -export class AuthenticationModule {} diff --git a/src/modules/oldauthentication/commands/add-username.command.ts b/src/modules/oldauthentication/commands/add-username.command.ts deleted file mode 100644 index 2036a23..0000000 --- a/src/modules/oldauthentication/commands/add-username.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AddUsernameRequest } from '../domain/dtos/add-username.request'; - -export class AddUsernameCommand { - readonly addUsernameRequest: AddUsernameRequest; - - constructor(request: AddUsernameRequest) { - this.addUsernameRequest = request; - } -} diff --git a/src/modules/oldauthentication/commands/create-authentication.command.ts b/src/modules/oldauthentication/commands/create-authentication.command.ts deleted file mode 100644 index 1d06deb..0000000 --- a/src/modules/oldauthentication/commands/create-authentication.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { CreateAuthenticationRequest } from '../domain/dtos/create-authentication.request'; - -export class CreateAuthenticationCommand { - readonly createAuthenticationRequest: CreateAuthenticationRequest; - - constructor(request: CreateAuthenticationRequest) { - this.createAuthenticationRequest = request; - } -} diff --git a/src/modules/oldauthentication/commands/delete-authentication.command.ts b/src/modules/oldauthentication/commands/delete-authentication.command.ts deleted file mode 100644 index bea3cd2..0000000 --- a/src/modules/oldauthentication/commands/delete-authentication.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DeleteAuthenticationRequest } from '../domain/dtos/delete-authentication.request'; - -export class DeleteAuthenticationCommand { - readonly deleteAuthenticationRequest: DeleteAuthenticationRequest; - - constructor(request: DeleteAuthenticationRequest) { - this.deleteAuthenticationRequest = request; - } -} diff --git a/src/modules/oldauthentication/commands/delete-username.command.ts b/src/modules/oldauthentication/commands/delete-username.command.ts deleted file mode 100644 index c46a3bd..0000000 --- a/src/modules/oldauthentication/commands/delete-username.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DeleteUsernameRequest } from '../domain/dtos/delete-username.request'; - -export class DeleteUsernameCommand { - readonly deleteUsernameRequest: DeleteUsernameRequest; - - constructor(request: DeleteUsernameRequest) { - this.deleteUsernameRequest = request; - } -} diff --git a/src/modules/oldauthentication/commands/update-password.command.ts b/src/modules/oldauthentication/commands/update-password.command.ts deleted file mode 100644 index 2120df1..0000000 --- a/src/modules/oldauthentication/commands/update-password.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UpdatePasswordRequest } from '../domain/dtos/update-password.request'; - -export class UpdatePasswordCommand { - readonly updatePasswordRequest: UpdatePasswordRequest; - - constructor(request: UpdatePasswordRequest) { - this.updatePasswordRequest = request; - } -} diff --git a/src/modules/oldauthentication/commands/update-username.command.ts b/src/modules/oldauthentication/commands/update-username.command.ts deleted file mode 100644 index 54a8415..0000000 --- a/src/modules/oldauthentication/commands/update-username.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UpdateUsernameRequest } from '../domain/dtos/update-username.request'; - -export class UpdateUsernameCommand { - readonly updateUsernameRequest: UpdateUsernameRequest; - - constructor(request: UpdateUsernameRequest) { - this.updateUsernameRequest = request; - } -} diff --git a/src/modules/oldauthentication/domain/dtos/add-username.request.ts b/src/modules/oldauthentication/domain/dtos/add-username.request.ts deleted file mode 100644 index 85a3f25..0000000 --- a/src/modules/oldauthentication/domain/dtos/add-username.request.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { Type } from './type.enum'; - -export class AddUsernameRequest { - @IsString() - @IsNotEmpty() - @AutoMap() - uuid: string; - - @IsString() - @IsNotEmpty() - @AutoMap() - username: string; - - @IsEnum(Type) - @IsNotEmpty() - @AutoMap() - type: Type; -} diff --git a/src/modules/oldauthentication/domain/dtos/create-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/create-authentication.request.ts deleted file mode 100644 index 7ca6562..0000000 --- a/src/modules/oldauthentication/domain/dtos/create-authentication.request.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { Type } from './type.enum'; - -export class CreateAuthenticationRequest { - @IsString() - @IsNotEmpty() - @AutoMap() - uuid: string; - - @IsString() - @IsNotEmpty() - @AutoMap() - username: string; - - @IsString() - @IsNotEmpty() - @AutoMap() - password: string; - - @IsEnum(Type) - @IsNotEmpty() - @AutoMap() - type: Type; -} diff --git a/src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts deleted file mode 100644 index 09ecb56..0000000 --- a/src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class DeleteAuthenticationRequest { - @IsString() - @IsNotEmpty() - @AutoMap() - uuid: string; -} diff --git a/src/modules/oldauthentication/domain/dtos/delete-username.request.ts b/src/modules/oldauthentication/domain/dtos/delete-username.request.ts deleted file mode 100644 index 3b2a221..0000000 --- a/src/modules/oldauthentication/domain/dtos/delete-username.request.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class DeleteUsernameRequest { - @IsString() - @IsNotEmpty() - @AutoMap() - username: string; -} diff --git a/src/modules/oldauthentication/domain/dtos/type.enum.ts b/src/modules/oldauthentication/domain/dtos/type.enum.ts deleted file mode 100644 index 2ef59cd..0000000 --- a/src/modules/oldauthentication/domain/dtos/type.enum.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Type { - EMAIL = 'EMAIL', - PHONE = 'PHONE', -} diff --git a/src/modules/oldauthentication/domain/dtos/update-password.request.ts b/src/modules/oldauthentication/domain/dtos/update-password.request.ts deleted file mode 100644 index bf4b6e4..0000000 --- a/src/modules/oldauthentication/domain/dtos/update-password.request.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class UpdatePasswordRequest { - @IsString() - @IsNotEmpty() - @AutoMap() - uuid: string; - - @IsString() - @IsNotEmpty() - @AutoMap() - password: string; -} diff --git a/src/modules/oldauthentication/domain/dtos/update-username.request.ts b/src/modules/oldauthentication/domain/dtos/update-username.request.ts deleted file mode 100644 index 47ccf9d..0000000 --- a/src/modules/oldauthentication/domain/dtos/update-username.request.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { Type } from './type.enum'; - -export class UpdateUsernameRequest { - @IsString() - @IsNotEmpty() - @AutoMap() - uuid: string; - - @IsString() - @IsNotEmpty() - @AutoMap() - username: string; - - @IsEnum(Type) - @IsNotEmpty() - @AutoMap() - type: Type; -} diff --git a/src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts deleted file mode 100644 index 997d67e..0000000 --- a/src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class ValidateAuthenticationRequest { - @IsString() - @IsNotEmpty() - username: string; - - @IsString() - @IsNotEmpty() - password: string; -} diff --git a/src/modules/oldauthentication/domain/entities/authentication.ts b/src/modules/oldauthentication/domain/entities/authentication.ts deleted file mode 100644 index 6f00964..0000000 --- a/src/modules/oldauthentication/domain/entities/authentication.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { AutoMap } from '@automapper/classes'; - -export class Authentication { - @AutoMap() - uuid: string; - - password: string; -} diff --git a/src/modules/oldauthentication/domain/entities/username.ts b/src/modules/oldauthentication/domain/entities/username.ts deleted file mode 100644 index 333c190..0000000 --- a/src/modules/oldauthentication/domain/entities/username.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AutoMap } from '@automapper/classes'; -import { Type } from '../dtos/type.enum'; - -export class Username { - @AutoMap() - uuid: string; - - @AutoMap() - username: string; - - @AutoMap() - type: Type; -} diff --git a/src/modules/oldauthentication/domain/interfaces/message-broker.ts b/src/modules/oldauthentication/domain/interfaces/message-broker.ts deleted file mode 100644 index 594aa43..0000000 --- a/src/modules/oldauthentication/domain/interfaces/message-broker.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export abstract class IMessageBroker { - exchange: string; - - constructor(exchange: string) { - this.exchange = exchange; - } - - abstract publish(routingKey: string, message: string): void; -} diff --git a/src/modules/oldauthentication/domain/usecases/add-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/add-username.usecase.ts deleted file mode 100644 index a8d5489..0000000 --- a/src/modules/oldauthentication/domain/usecases/add-username.usecase.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CommandHandler } from '@nestjs/cqrs'; -import { Messager } from '../../adapters/secondaries/messager'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { AddUsernameCommand } from '../../commands/add-username.command'; -import { Username } from '../entities/username'; - -@CommandHandler(AddUsernameCommand) -export class AddUsernameUseCase { - constructor( - private readonly usernameRepository: UsernameRepository, - private readonly messager: Messager, - ) {} - - execute = async (command: AddUsernameCommand): Promise => { - const { uuid, username, type } = command.addUsernameRequest; - try { - return await this.usernameRepository.create({ - uuid, - type, - username, - }); - } catch (error) { - this.messager.publish( - 'logging.auth.username.add.warning', - JSON.stringify({ - command, - error, - }), - ); - throw error; - } - }; -} diff --git a/src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts deleted file mode 100644 index 9ca07e4..0000000 --- a/src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { CommandHandler } from '@nestjs/cqrs'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { CreateAuthenticationCommand } from '../../commands/create-authentication.command'; -import { Authentication } from '../entities/authentication'; -import * as bcrypt from 'bcrypt'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Messager } from '../../adapters/secondaries/messager'; - -@CommandHandler(CreateAuthenticationCommand) -export class CreateAuthenticationUseCase { - constructor( - private readonly authenticationRepository: AuthenticationRepository, - private readonly usernameRepository: UsernameRepository, - private readonly messager: Messager, - ) {} - - execute = async ( - command: CreateAuthenticationCommand, - ): Promise => { - const { uuid, password, ...username } = command.createAuthenticationRequest; - const hash = await bcrypt.hash(password, 10); - - try { - const auth = await this.authenticationRepository.create({ - uuid, - password: hash, - }); - - await this.usernameRepository.create({ - uuid, - ...username, - }); - - return auth; - } catch (error) { - this.messager.publish( - 'logging.auth.create.crit', - JSON.stringify({ - command, - error, - }), - ); - throw error; - } - }; -} diff --git a/src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts deleted file mode 100644 index e429a5d..0000000 --- a/src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { CommandHandler } from '@nestjs/cqrs'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { Messager } from '../../adapters/secondaries/messager'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { DeleteAuthenticationCommand } from '../../commands/delete-authentication.command'; -import { Authentication } from '../entities/authentication'; - -@CommandHandler(DeleteAuthenticationCommand) -export class DeleteAuthenticationUseCase { - constructor( - private readonly authenticationRepository: AuthenticationRepository, - private readonly usernameRepository: UsernameRepository, - private readonly messager: Messager, - ) {} - - execute = async ( - command: DeleteAuthenticationCommand, - ): Promise => { - try { - await this.usernameRepository.deleteMany({ - uuid: command.deleteAuthenticationRequest.uuid, - }); - return await this.authenticationRepository.delete( - command.deleteAuthenticationRequest.uuid, - ); - } catch (error) { - this.messager.publish( - 'logging.auth.delete.crit', - JSON.stringify({ - command, - error, - }), - ); - throw error; - } - }; -} diff --git a/src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts deleted file mode 100644 index 76d3430..0000000 --- a/src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { UnauthorizedException } from '@nestjs/common'; -import { CommandHandler } from '@nestjs/cqrs'; -import { Messager } from '../../adapters/secondaries/messager'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { DeleteUsernameCommand } from '../../commands/delete-username.command'; - -@CommandHandler(DeleteUsernameCommand) -export class DeleteUsernameUseCase { - constructor( - private readonly usernameRepository: UsernameRepository, - private readonly messager: Messager, - ) {} - - execute = async (command: DeleteUsernameCommand): Promise => { - try { - const { username } = command.deleteUsernameRequest; - const usernameFound = await this.usernameRepository.findOne({ - username, - }); - const usernames = await this.usernameRepository.findAll(1, 1, { - uuid: usernameFound.uuid, - }); - if (usernames.total > 1) { - return await this.usernameRepository.deleteMany({ username }); - } - throw new UnauthorizedException(); - } catch (error) { - this.messager.publish( - 'logging.auth.username.delete.warning', - JSON.stringify({ - command, - error, - }), - ); - throw error; - } - }; -} diff --git a/src/modules/oldauthentication/domain/usecases/update-password.usecase.ts b/src/modules/oldauthentication/domain/usecases/update-password.usecase.ts deleted file mode 100644 index 5809f54..0000000 --- a/src/modules/oldauthentication/domain/usecases/update-password.usecase.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CommandHandler } from '@nestjs/cqrs'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { Authentication } from '../entities/authentication'; -import * as bcrypt from 'bcrypt'; -import { UpdatePasswordCommand } from '../../commands/update-password.command'; -import { Messager } from '../../adapters/secondaries/messager'; - -@CommandHandler(UpdatePasswordCommand) -export class UpdatePasswordUseCase { - constructor( - private readonly authenticationRepository: AuthenticationRepository, - private readonly messager: Messager, - ) {} - - execute = async (command: UpdatePasswordCommand): Promise => { - const { uuid, password } = command.updatePasswordRequest; - const hash = await bcrypt.hash(password, 10); - - try { - return await this.authenticationRepository.update(uuid, { - password: hash, - }); - } catch (error) { - this.messager.publish( - 'logging.auth.password.update.warning', - JSON.stringify({ - command, - error, - }), - ); - throw error; - } - }; -} diff --git a/src/modules/oldauthentication/domain/usecases/update-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/update-username.usecase.ts deleted file mode 100644 index 7870bf0..0000000 --- a/src/modules/oldauthentication/domain/usecases/update-username.usecase.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Mapper } from '@automapper/core'; -import { InjectMapper } from '@automapper/nestjs'; -import { BadRequestException } from '@nestjs/common'; -import { CommandBus, CommandHandler } from '@nestjs/cqrs'; -import { Messager } from '../../adapters/secondaries/messager'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { AddUsernameCommand } from '../../commands/add-username.command'; -import { UpdateUsernameCommand } from '../../commands/update-username.command'; -import { AddUsernameRequest } from '../dtos/add-username.request'; -import { UpdateUsernameRequest } from '../dtos/update-username.request'; -import { Username } from '../entities/username'; - -@CommandHandler(UpdateUsernameCommand) -export class UpdateUsernameUseCase { - constructor( - private readonly usernameRepository: UsernameRepository, - private readonly commandBus: CommandBus, - @InjectMapper() private readonly mapper: Mapper, - private readonly messager: Messager, - ) {} - - execute = async (command: UpdateUsernameCommand): Promise => { - const { uuid, username, type } = command.updateUsernameRequest; - if (!username) throw new BadRequestException(); - // update username if it exists, otherwise create it - const existingUsername = await this.usernameRepository.findOne({ - uuid, - type, - }); - if (existingUsername) { - try { - return await this.usernameRepository.updateWhere( - { - uuid_type: { - uuid, - type, - }, - }, - { - username, - }, - ); - } catch (error) { - this.messager.publish( - 'logging.auth.username.update.warning', - JSON.stringify({ - command, - error, - }), - ); - throw error; - } - } - const addUsernameRequest = this.mapper.map( - command.updateUsernameRequest, - UpdateUsernameRequest, - AddUsernameRequest, - ); - try { - return await this.commandBus.execute( - new AddUsernameCommand(addUsernameRequest), - ); - } catch (e) { - throw e; - } - }; -} diff --git a/src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts deleted file mode 100644 index 56829e9..0000000 --- a/src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { QueryHandler } from '@nestjs/cqrs'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { ValidateAuthenticationQuery as ValidateAuthenticationQuery } from '../../queries/validate-authentication.query'; -import { Authentication } from '../entities/authentication'; -import * as bcrypt from 'bcrypt'; -import { NotFoundException, UnauthorizedException } from '@nestjs/common'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Username } from '../entities/username'; - -@QueryHandler(ValidateAuthenticationQuery) -export class ValidateAuthenticationUseCase { - constructor( - private readonly authenticationRepository: AuthenticationRepository, - private readonly usernameRepository: UsernameRepository, - ) {} - - execute = async ( - validate: ValidateAuthenticationQuery, - ): Promise => { - let username = new Username(); - try { - username = await this.usernameRepository.findOne({ - username: validate.username, - }); - } catch (e) { - throw new NotFoundException(); - } - try { - const auth = await this.authenticationRepository.findOne({ - uuid: username.uuid, - }); - if (auth) { - const isMatch = await bcrypt.compare(validate.password, auth.password); - if (isMatch) return auth; - } - throw new UnauthorizedException(); - } catch (e) { - throw new UnauthorizedException(); - } - }; -} diff --git a/src/modules/oldauthentication/mappers/authentication.profile.ts b/src/modules/oldauthentication/mappers/authentication.profile.ts deleted file mode 100644 index ab9156b..0000000 --- a/src/modules/oldauthentication/mappers/authentication.profile.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createMap, Mapper } from '@automapper/core'; -import { AutomapperProfile, InjectMapper } from '@automapper/nestjs'; -import { Injectable } from '@nestjs/common'; -import { AuthenticationPresenter } from '../adapters/primaries/authentication.presenter'; -import { Authentication } from '../domain/entities/authentication'; - -@Injectable() -export class AuthenticationProfile extends AutomapperProfile { - constructor(@InjectMapper() mapper: Mapper) { - super(mapper); - } - - override get profile() { - return (mapper: any) => { - createMap(mapper, Authentication, AuthenticationPresenter); - }; - } -} diff --git a/src/modules/oldauthentication/mappers/username.profile.ts b/src/modules/oldauthentication/mappers/username.profile.ts deleted file mode 100644 index af2cc76..0000000 --- a/src/modules/oldauthentication/mappers/username.profile.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createMap, Mapper } from '@automapper/core'; -import { AutomapperProfile, InjectMapper } from '@automapper/nestjs'; -import { Injectable } from '@nestjs/common'; -import { UsernamePresenter } from '../adapters/primaries/username.presenter'; -import { AddUsernameRequest } from '../domain/dtos/add-username.request'; -import { UpdateUsernameRequest } from '../domain/dtos/update-username.request'; -import { Username } from '../domain/entities/username'; - -@Injectable() -export class UsernameProfile extends AutomapperProfile { - constructor(@InjectMapper() mapper: Mapper) { - super(mapper); - } - - override get profile() { - return (mapper: any) => { - createMap(mapper, Username, UsernamePresenter); - createMap(mapper, UpdateUsernameRequest, AddUsernameRequest); - }; - } -} diff --git a/src/modules/oldauthentication/queries/validate-authentication.query.ts b/src/modules/oldauthentication/queries/validate-authentication.query.ts deleted file mode 100644 index 32bb41c..0000000 --- a/src/modules/oldauthentication/queries/validate-authentication.query.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class ValidateAuthenticationQuery { - readonly username: string; - readonly password: string; - - constructor(username: string, password: string) { - this.username = username; - this.password = password; - } -} diff --git a/src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts b/src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts deleted file mode 100644 index ec29f55..0000000 --- a/src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { TestingModule, Test } from '@nestjs/testing'; -import { DatabaseModule } from '../../../database/database.module'; -import { PrismaService } from '../../../database/adapters/secondaries/prisma-service'; -import { DatabaseException } from '../../../database/exceptions/database.exception'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { v4 } from 'uuid'; -import * as bcrypt from 'bcrypt'; -import { Authentication } from '../../domain/entities/authentication'; - -describe('AuthenticationRepository', () => { - let prismaService: PrismaService; - let authenticationRepository: AuthenticationRepository; - - const createAuthentications = async (nbToCreate = 10) => { - for (let i = 0; i < nbToCreate; i++) { - await prismaService.auth.create({ - data: { - uuid: v4(), - password: bcrypt.hashSync(`password-${i}`, 10), - }, - }); - } - }; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [DatabaseModule], - providers: [AuthenticationRepository, PrismaService], - }).compile(); - - prismaService = module.get(PrismaService); - authenticationRepository = module.get( - AuthenticationRepository, - ); - }); - - afterAll(async () => { - await prismaService.$disconnect(); - }); - - beforeEach(async () => { - await prismaService.auth.deleteMany(); - }); - - describe('findAll', () => { - it('should return an empty data array', async () => { - const res = await authenticationRepository.findAll(); - expect(res).toEqual({ - data: [], - total: 0, - }); - }); - - it('should return a data array with 8 auths', async () => { - await createAuthentications(8); - const auths = await authenticationRepository.findAll(); - expect(auths.data.length).toBe(8); - expect(auths.total).toBe(8); - }); - - it('should return a data array limited to 10 authentications', async () => { - await createAuthentications(20); - const auths = await authenticationRepository.findAll(); - expect(auths.data.length).toBe(10); - expect(auths.total).toBe(20); - }); - }); - - describe('findOneByUuid', () => { - it('should return an authentication', async () => { - const authToFind = await prismaService.auth.create({ - data: { - uuid: v4(), - password: bcrypt.hashSync(`password`, 10), - }, - }); - - const auth = await authenticationRepository.findOneByUuid( - authToFind.uuid, - ); - expect(auth.uuid).toBe(authToFind.uuid); - }); - - it('should return null', async () => { - const auth = await authenticationRepository.findOneByUuid( - '544572be-11fb-4244-8235-587221fc9104', - ); - expect(auth).toBeNull(); - }); - }); - - describe('create', () => { - it('should create an authentication', async () => { - const beforeCount = await prismaService.auth.count(); - - const authenticationToCreate: Authentication = new Authentication(); - authenticationToCreate.uuid = v4(); - authenticationToCreate.password = bcrypt.hashSync(`password`, 10); - const authentication = await authenticationRepository.create( - authenticationToCreate, - ); - - const afterCount = await prismaService.auth.count(); - - expect(afterCount - beforeCount).toBe(1); - expect(authentication.uuid).toBeDefined(); - }); - }); - - describe('update', () => { - it('should update authentication password', async () => { - const authenticationToUpdate = await prismaService.auth.create({ - data: { - uuid: v4(), - password: bcrypt.hashSync(`password`, 10), - }, - }); - - const toUpdate: Authentication = new Authentication(); - toUpdate.password = bcrypt.hashSync(`newPassword`, 10); - const updatedAuthentication = await authenticationRepository.update( - authenticationToUpdate.uuid, - toUpdate, - ); - - expect(updatedAuthentication.uuid).toBe(authenticationToUpdate.uuid); - }); - - it('should throw DatabaseException', async () => { - const toUpdate: Authentication = new Authentication(); - toUpdate.password = bcrypt.hashSync(`newPassword`, 10); - - await expect( - authenticationRepository.update( - '544572be-11fb-4244-8235-587221fc9104', - toUpdate, - ), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('delete', () => { - it('should delete an authentication', async () => { - const authenticationToRemove = await prismaService.auth.create({ - data: { - uuid: v4(), - password: bcrypt.hashSync(`password`, 10), - }, - }); - await authenticationRepository.delete(authenticationToRemove.uuid); - - const count = await prismaService.auth.count(); - expect(count).toBe(0); - }); - - it('should throw DatabaseException', async () => { - await expect( - authenticationRepository.delete('544572be-11fb-4244-8235-587221fc9104'), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/integration/username.repository.spec.ts b/src/modules/oldauthentication/tests/integration/username.repository.spec.ts deleted file mode 100644 index ccb1e3c..0000000 --- a/src/modules/oldauthentication/tests/integration/username.repository.spec.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { TestingModule, Test } from '@nestjs/testing'; -import { DatabaseModule } from '../../../database/database.module'; -import { PrismaService } from '../../../database/adapters/secondaries/prisma-service'; -import { DatabaseException } from '../../../database/exceptions/database.exception'; -import { v4 } from 'uuid'; -import { Type } from '../../domain/dtos/type.enum'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Username } from '../../domain/entities/username'; - -describe('UsernameRepository', () => { - let prismaService: PrismaService; - let usernameRepository: UsernameRepository; - - const createUsernames = async (nbToCreate = 10) => { - for (let i = 0; i < nbToCreate; i++) { - await prismaService.username.create({ - data: { - uuid: v4(), - username: `john.doe.${i}@email.com`, - type: Type.EMAIL, - }, - }); - } - }; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [DatabaseModule], - providers: [UsernameRepository, PrismaService], - }).compile(); - - prismaService = module.get(PrismaService); - usernameRepository = module.get(UsernameRepository); - }); - - afterAll(async () => { - await prismaService.$disconnect(); - }); - - beforeEach(async () => { - await prismaService.username.deleteMany(); - }); - - describe('findAll', () => { - it('should return an empty data array', async () => { - const res = await usernameRepository.findAll(); - expect(res).toEqual({ - data: [], - total: 0, - }); - }); - - it('should return a data array with 8 usernames', async () => { - await createUsernames(8); - const usernames = await usernameRepository.findAll(); - expect(usernames.data.length).toBe(8); - expect(usernames.total).toBe(8); - }); - - it('should return a data array limited to 10 usernames', async () => { - await createUsernames(20); - const usernames = await usernameRepository.findAll(); - expect(usernames.data.length).toBe(10); - expect(usernames.total).toBe(20); - }); - }); - - describe('findOne', () => { - it('should return a username with uuid and email', async () => { - const usernameToFind = await prismaService.username.create({ - data: { - uuid: v4(), - username: 'john.doe@email.com', - type: Type.EMAIL, - }, - }); - - const username = await usernameRepository.findOne({ - username: 'john.doe@email.com', - type: Type.EMAIL, - }); - expect(username.uuid).toBe(usernameToFind.uuid); - }); - - it('should return null', async () => { - const username = await usernameRepository.findOne({ - username: 'jane.doe@email.com', - type: Type.EMAIL, - }); - expect(username).toBeNull(); - }); - }); - - describe('create', () => { - it('should create a username with an email', async () => { - const beforeCount = await prismaService.username.count(); - - const usernameToCreate: Username = new Username(); - usernameToCreate.uuid = v4(); - usernameToCreate.username = 'john.doe@email.com'; - usernameToCreate.type = Type.EMAIL; - const username = await usernameRepository.create(usernameToCreate); - - const afterCount = await prismaService.username.count(); - - expect(afterCount - beforeCount).toBe(1); - expect(username.uuid).toBeDefined(); - }); - it('should create a username with a phone number', async () => { - const beforeCount = await prismaService.username.count(); - - const usernameToCreate: Username = new Username(); - usernameToCreate.uuid = v4(); - usernameToCreate.username = '+33611223344'; - usernameToCreate.type = Type.PHONE; - const username = await usernameRepository.create(usernameToCreate); - - const afterCount = await prismaService.username.count(); - - expect(afterCount - beforeCount).toBe(1); - expect(username.uuid).toBeDefined(); - }); - it('should create a username with an email for an existing uuid', async () => { - const beforeCount = await prismaService.username.count(); - - const uuid = v4(); - - const firstUsernameToCreate: Username = new Username(); - firstUsernameToCreate.uuid = uuid; - firstUsernameToCreate.username = '+33611223344'; - firstUsernameToCreate.type = Type.PHONE; - const firstUsername = await usernameRepository.create( - firstUsernameToCreate, - ); - - const secondUsernameToCreate: Username = new Username(); - secondUsernameToCreate.uuid = uuid; - secondUsernameToCreate.username = 'john.doe@email.com'; - secondUsernameToCreate.type = Type.EMAIL; - const secondUsername = await usernameRepository.create( - secondUsernameToCreate, - ); - - const afterCount = await prismaService.username.count(); - - expect(afterCount - beforeCount).toBe(2); - expect(firstUsername.uuid).toEqual(secondUsername.uuid); - }); - it('should throw DatabaseException if username already exists for a given type', async () => { - const uuid = v4(); - - const firstUsernameToCreate: Username = new Username(); - firstUsernameToCreate.uuid = uuid; - firstUsernameToCreate.username = 'john.doe@email.com'; - firstUsernameToCreate.type = Type.EMAIL; - await usernameRepository.create(firstUsernameToCreate); - - const secondUsernameToCreate: Username = new Username(); - secondUsernameToCreate.uuid = uuid; - secondUsernameToCreate.username = 'jane.doe@email.com'; - secondUsernameToCreate.type = Type.EMAIL; - - await expect( - usernameRepository.create(secondUsernameToCreate), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('update', () => { - it('should update username email', async () => { - const usernameToUpdate = await prismaService.username.create({ - data: { - uuid: v4(), - username: `john.doe@email.com`, - type: Type.EMAIL, - }, - }); - - const toUpdate: Username = new Username(); - toUpdate.username = 'jane.doe@email.com'; - const updatedUsername = await usernameRepository.updateWhere( - { - uuid_type: { - uuid: usernameToUpdate.uuid, - type: usernameToUpdate.type, - }, - }, - { - username: toUpdate.username, - }, - ); - - expect(updatedUsername.uuid).toBe(usernameToUpdate.uuid); - expect(updatedUsername.username).toBe('jane.doe@email.com'); - }); - - it('should update username phone', async () => { - const usernameToUpdate = await prismaService.username.create({ - data: { - uuid: v4(), - username: `+33611223344`, - type: Type.PHONE, - }, - }); - - const toUpdate: Username = new Username(); - toUpdate.username = '+33622334455'; - const updatedUsername = await usernameRepository.updateWhere( - { - uuid_type: { - uuid: usernameToUpdate.uuid, - type: usernameToUpdate.type, - }, - }, - { - username: toUpdate.username, - }, - ); - - expect(updatedUsername.uuid).toBe(usernameToUpdate.uuid); - expect(updatedUsername.username).toBe('+33622334455'); - }); - it('should throw DatabaseException if email not found', async () => { - const toUpdate: Username = new Username(); - toUpdate.username = 'jane.doe@email.com'; - - await expect( - usernameRepository.updateWhere( - { - uuid_type: { - uuid: '544572be-11fb-4244-8235-587221fc9104', - type: Type.EMAIL, - }, - }, - { - username: toUpdate.username, - }, - ), - ).rejects.toBeInstanceOf(DatabaseException); - }); - it('should throw DatabaseException if phone not found', async () => { - const toUpdate: Username = new Username(); - toUpdate.username = '+33611223344'; - - await expect( - usernameRepository.updateWhere( - { - uuid_type: { - uuid: '544572be-11fb-4244-8235-587221fc9104', - type: Type.PHONE, - }, - }, - { - username: toUpdate.username, - }, - ), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); - - describe('delete', () => { - it('should delete a username', async () => { - const usernameToRemove = await prismaService.username.create({ - data: { - uuid: v4(), - username: `+33611223344`, - type: Type.PHONE, - }, - }); - await usernameRepository.deleteMany({ uuid: usernameToRemove.uuid }); - - const count = await prismaService.username.count(); - expect(count).toBe(0); - }); - - it('should throw DatabaseException', async () => { - await expect( - usernameRepository.delete('544572be-11fb-4244-8235-587221fc9104'), - ).rejects.toBeInstanceOf(DatabaseException); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts deleted file mode 100644 index 4c5ddde..0000000 --- a/src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthenticationProfile } from '../../mappers/authentication.profile'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Username } from '../../domain/entities/username'; -import { Type } from '../../domain/dtos/type.enum'; -import { AddUsernameRequest } from '../../domain/dtos/add-username.request'; -import { AddUsernameCommand } from '../../commands/add-username.command'; -import { AddUsernameUseCase } from '../../domain/usecases/add-username.usecase'; -import { Messager } from '../../adapters/secondaries/messager'; - -const addUsernameRequest: AddUsernameRequest = { - uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', - username: '0611223344', - type: Type.PHONE, -}; -const addUsernameCommand: AddUsernameCommand = new AddUsernameCommand( - addUsernameRequest, -); - -const mockUsernameRepository = { - create: jest - .fn() - .mockImplementationOnce(() => { - return Promise.resolve(addUsernameRequest); - }) - .mockImplementation(() => { - throw new Error('Already exists'); - }), -}; - -const mockMessager = { - publish: jest.fn().mockImplementation(), -}; - -describe('AddUsernameUseCase', () => { - let addUsernameUseCase: AddUsernameUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: UsernameRepository, - useValue: mockUsernameRepository, - }, - { - provide: Messager, - useValue: mockMessager, - }, - AddUsernameUseCase, - AuthenticationProfile, - ], - }).compile(); - - addUsernameUseCase = module.get(AddUsernameUseCase); - }); - - it('should be defined', () => { - expect(addUsernameUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should add a username for phone type', async () => { - const addedUsername: Username = await addUsernameUseCase.execute( - addUsernameCommand, - ); - - expect(addedUsername.username).toBe(addUsernameRequest.username); - expect(addedUsername.type).toBe(addUsernameRequest.type); - }); - it('should throw an error if user already exists', async () => { - await expect( - addUsernameUseCase.execute(addUsernameCommand), - ).rejects.toBeInstanceOf(Error); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts deleted file mode 100644 index 2ee6e9c..0000000 --- a/src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { CreateAuthenticationCommand } from '../../commands/create-authentication.command'; -import { CreateAuthenticationRequest } from '../../domain/dtos/create-authentication.request'; -import { Authentication } from '../../domain/entities/authentication'; -import { CreateAuthenticationUseCase } from '../../domain/usecases/create-authentication.usecase'; -import * as bcrypt from 'bcrypt'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Type } from '../../domain/dtos/type.enum'; -import { Messager } from '../../adapters/secondaries/messager'; - -const newAuthenticationRequest: CreateAuthenticationRequest = - new CreateAuthenticationRequest(); -newAuthenticationRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91'; -newAuthenticationRequest.username = 'john.doe@email.com'; -newAuthenticationRequest.password = 'John123'; -newAuthenticationRequest.type = Type.EMAIL; -const newAuthCommand: CreateAuthenticationCommand = - new CreateAuthenticationCommand(newAuthenticationRequest); - -const mockAuthenticationRepository = { - create: jest - .fn() - .mockImplementationOnce(() => { - return Promise.resolve({ - uuid: newAuthenticationRequest.uuid, - password: bcrypt.hashSync(newAuthenticationRequest.password, 10), - }); - }) - .mockImplementation(() => { - throw new Error('Already exists'); - }), -}; - -const mockUsernameRepository = { - create: jest.fn().mockResolvedValue({ - uuid: newAuthenticationRequest.uuid, - username: newAuthenticationRequest.username, - type: newAuthenticationRequest.type, - }), -}; - -const mockMessager = { - publish: jest.fn().mockImplementation(), -}; - -describe('CreateAuthenticationUseCase', () => { - let createAuthenticationUseCase: CreateAuthenticationUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: AuthenticationRepository, - useValue: mockAuthenticationRepository, - }, - { - provide: UsernameRepository, - useValue: mockUsernameRepository, - }, - { - provide: Messager, - useValue: mockMessager, - }, - CreateAuthenticationUseCase, - ], - }).compile(); - - createAuthenticationUseCase = module.get( - CreateAuthenticationUseCase, - ); - }); - - it('should be defined', () => { - expect(createAuthenticationUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should create an authentication with an encrypted password', async () => { - const newAuthentication: Authentication = - await createAuthenticationUseCase.execute(newAuthCommand); - - expect( - bcrypt.compareSync( - newAuthenticationRequest.password, - newAuthentication.password, - ), - ).toBeTruthy(); - }); - it('should throw an error if user already exists', async () => { - await expect( - createAuthenticationUseCase.execute(newAuthCommand), - ).rejects.toBeInstanceOf(Error); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts deleted file mode 100644 index f3bda36..0000000 --- a/src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { Messager } from '../../adapters/secondaries/messager'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { DeleteAuthenticationCommand } from '../../commands/delete-authentication.command'; -import { DeleteAuthenticationRequest } from '../../domain/dtos/delete-authentication.request'; -import { Type } from '../../domain/dtos/type.enum'; -import { DeleteAuthenticationUseCase } from '../../domain/usecases/delete-authentication.usecase'; - -const usernames = { - data: [ - { - uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e', - username: 'john.doe@email.com', - type: Type.EMAIL, - }, - { - uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e', - username: '0611223344', - type: Type.PHONE, - }, - ], - total: 2, -}; - -const deleteAuthenticationRequest: DeleteAuthenticationRequest = - new DeleteAuthenticationRequest(); -deleteAuthenticationRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91'; -const deleteAuthenticationCommand: DeleteAuthenticationCommand = - new DeleteAuthenticationCommand(deleteAuthenticationRequest); - -const mockAuthenticationRepository = { - delete: jest - .fn() - .mockResolvedValueOnce(undefined) - .mockImplementation(() => { - throw new Error('Error'); - }), -}; - -const mockUsernameRepository = { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - findAll: jest.fn().mockImplementation((page, perPage, query) => { - return Promise.resolve(usernames); - }), - delete: jest.fn().mockResolvedValue(undefined), - deleteMany: jest.fn().mockResolvedValue(undefined), -}; - -const mockMessager = { - publish: jest.fn().mockImplementation(), -}; - -describe('DeleteAuthenticationUseCase', () => { - let deleteAuthenticationUseCase: DeleteAuthenticationUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: AuthenticationRepository, - useValue: mockAuthenticationRepository, - }, - { - provide: UsernameRepository, - useValue: mockUsernameRepository, - }, - { - provide: Messager, - useValue: mockMessager, - }, - DeleteAuthenticationUseCase, - ], - }).compile(); - - deleteAuthenticationUseCase = module.get( - DeleteAuthenticationUseCase, - ); - }); - - it('should be defined', () => { - expect(deleteAuthenticationUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should delete an authentication and its usernames', async () => { - const deletedAuthentication = await deleteAuthenticationUseCase.execute( - deleteAuthenticationCommand, - ); - - expect(deletedAuthentication).toBe(undefined); - }); - it('should throw an error if authentication does not exist', async () => { - await expect( - deleteAuthenticationUseCase.execute(deleteAuthenticationCommand), - ).rejects.toBeInstanceOf(Error); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts deleted file mode 100644 index 854479e..0000000 --- a/src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { UnauthorizedException } from '@nestjs/common'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Messager } from '../../adapters/secondaries/messager'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { DeleteUsernameCommand } from '../../commands/delete-username.command'; -import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request'; -import { Type } from '../../domain/dtos/type.enum'; -import { DeleteUsernameUseCase } from '../../domain/usecases/delete-username.usecase'; - -const usernamesEmail = { - data: [ - { - uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e', - username: 'john.doe@email.com', - type: Type.EMAIL, - }, - { - uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e', - username: '0611223344', - type: Type.PHONE, - }, - ], - total: 2, -}; - -const usernamesPhone = { - data: [ - { - uuid: 'a7fa221f-dd77-481c-bb77-ae89da662c87', - username: '0611223344', - type: Type.PHONE, - }, - ], - total: 1, -}; - -const deleteUsernameEmailRequest: DeleteUsernameRequest = - new DeleteUsernameRequest(); -deleteUsernameEmailRequest.username = 'john.doe@email.com'; - -const deleteUsernamePhoneRequest: DeleteUsernameRequest = - new DeleteUsernameRequest(); -deleteUsernamePhoneRequest.username = '0611223344'; - -const deleteUsernameEmailCommand: DeleteUsernameCommand = - new DeleteUsernameCommand(deleteUsernameEmailRequest); - -const deleteUsernamePhoneCommand: DeleteUsernameCommand = - new DeleteUsernameCommand(deleteUsernamePhoneRequest); - -const mockUsernameRepository = { - findOne: jest.fn().mockImplementation((where) => { - if (where.username == 'john.doe@email.com') { - return { uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e' }; - } - return { uuid: 'a7fa221f-dd77-481c-bb77-ae89da662c87' }; - }), - findAll: jest.fn().mockImplementation((page, perPage, query) => { - if (query.uuid == 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e') { - return Promise.resolve(usernamesEmail); - } - return Promise.resolve(usernamesPhone); - }), - deleteMany: jest.fn().mockResolvedValue(undefined), -}; - -const mockMessager = { - publish: jest.fn().mockImplementation(), -}; - -describe('DeleteUsernameUseCase', () => { - let deleteUsernameUseCase: DeleteUsernameUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: UsernameRepository, - useValue: mockUsernameRepository, - }, - { - provide: Messager, - useValue: mockMessager, - }, - DeleteUsernameUseCase, - ], - }).compile(); - - deleteUsernameUseCase = module.get( - DeleteUsernameUseCase, - ); - }); - - it('should be defined', () => { - expect(deleteUsernameUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should delete a username', async () => { - const deletedEmailUsername = await deleteUsernameUseCase.execute( - deleteUsernameEmailCommand, - ); - expect(deletedEmailUsername).toBe(undefined); - }); - - it('should throw an exception if auth has only one username', async () => { - await expect( - deleteUsernameUseCase.execute(deleteUsernamePhoneCommand), - ).rejects.toBeInstanceOf(UnauthorizedException); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/messager.spec.ts b/src/modules/oldauthentication/tests/unit/messager.spec.ts deleted file mode 100644 index 97107d1..0000000 --- a/src/modules/oldauthentication/tests/unit/messager.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { ConfigService } from '@nestjs/config'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Messager } from '../../adapters/secondaries/messager'; - -const mockAmqpConnection = { - publish: jest.fn().mockImplementation(), -}; - -const mockConfigService = { - get: jest.fn().mockResolvedValue({ - RMQ_EXCHANGE: 'mobicoop', - }), -}; - -describe('Messager', () => { - let messager: Messager; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [], - providers: [ - Messager, - { - provide: AmqpConnection, - useValue: mockAmqpConnection, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - messager = module.get(Messager); - }); - - it('should be defined', () => { - expect(messager).toBeDefined(); - }); - - it('should publish a message', async () => { - jest.spyOn(mockAmqpConnection, 'publish'); - messager.publish('authentication.create.info', 'my-test'); - expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts deleted file mode 100644 index 982ea1e..0000000 --- a/src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { Authentication } from '../../domain/entities/authentication'; -import * as bcrypt from 'bcrypt'; -import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request'; -import { UpdatePasswordCommand } from '../../commands/update-password.command'; -import { UpdatePasswordUseCase } from '../../domain/usecases/update-password.usecase'; -import { Messager } from '../../adapters/secondaries/messager'; - -const updatePasswordRequest: UpdatePasswordRequest = - new UpdatePasswordRequest(); -updatePasswordRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91'; -updatePasswordRequest.password = 'John123'; - -const updatePasswordCommand: UpdatePasswordCommand = new UpdatePasswordCommand( - updatePasswordRequest, -); - -const mockAuthenticationRepository = { - update: jest - .fn() - .mockResolvedValueOnce({ - uuid: updatePasswordRequest.uuid, - password: bcrypt.hashSync(updatePasswordRequest.password, 10), - }) - .mockImplementation(() => { - throw new Error('Error'); - }), -}; - -const mockMessager = { - publish: jest.fn().mockImplementation(), -}; - -describe('UpdatePasswordUseCase', () => { - let updatePasswordUseCase: UpdatePasswordUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: AuthenticationRepository, - useValue: mockAuthenticationRepository, - }, - { - provide: Messager, - useValue: mockMessager, - }, - UpdatePasswordUseCase, - ], - }).compile(); - - updatePasswordUseCase = module.get( - UpdatePasswordUseCase, - ); - }); - - it('should be defined', () => { - expect(updatePasswordUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should update an auth with an new encrypted password', async () => { - const newAuth: Authentication = await updatePasswordUseCase.execute( - updatePasswordCommand, - ); - - expect( - bcrypt.compareSync(updatePasswordRequest.password, newAuth.password), - ).toBeTruthy(); - }); - it('should throw an error if auth does not exist', async () => { - await expect( - updatePasswordUseCase.execute(updatePasswordCommand), - ).rejects.toBeInstanceOf(Error); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts deleted file mode 100644 index ca3f8b3..0000000 --- a/src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Username } from '../../domain/entities/username'; -import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request'; -import { UpdateUsernameCommand } from '../../commands/update-username.command'; -import { Type } from '../../domain/dtos/type.enum'; -import { UpdateUsernameUseCase } from '../../domain/usecases/update-username.usecase'; -import { CommandBus } from '@nestjs/cqrs'; -import { UsernameProfile } from '../../mappers/username.profile'; -import { BadRequestException } from '@nestjs/common'; -import { Messager } from '../../adapters/secondaries/messager'; - -const existingUsername = { - uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', - username: 'john.doe@email.com', - type: Type.EMAIL, -}; - -const newUsernameRequest: UpdateUsernameRequest = new UpdateUsernameRequest(); -newUsernameRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a90'; -newUsernameRequest.username = '+33611223344'; -newUsernameRequest.type = Type.PHONE; - -const updateUsernameRequest: UpdateUsernameRequest = - new UpdateUsernameRequest(); -updateUsernameRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91'; -updateUsernameRequest.username = 'johnny.doe@email.com'; -updateUsernameRequest.type = Type.EMAIL; - -const unknownUsernameRequest: UpdateUsernameRequest = - new UpdateUsernameRequest(); -unknownUsernameRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a92'; -unknownUsernameRequest.username = 'unknown@email.com'; -unknownUsernameRequest.type = Type.EMAIL; - -const invalidUpdateUsernameRequest: UpdateUsernameRequest = - new UpdateUsernameRequest(); -invalidUpdateUsernameRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a93'; -invalidUpdateUsernameRequest.username = ''; -invalidUpdateUsernameRequest.type = Type.EMAIL; - -const newUsernameCommand: UpdateUsernameCommand = new UpdateUsernameCommand( - newUsernameRequest, -); - -const updateUsernameCommand: UpdateUsernameCommand = new UpdateUsernameCommand( - updateUsernameRequest, -); - -const invalidUpdateUsernameCommand: UpdateUsernameCommand = - new UpdateUsernameCommand(invalidUpdateUsernameRequest); - -const unknownUpdateUsernameCommand: UpdateUsernameCommand = - new UpdateUsernameCommand(unknownUsernameRequest); - -const mockUsernameRepository = { - findOne: jest.fn().mockImplementation((request) => { - if (request.uuid == 'bb281075-1b98-4456-89d6-c643d3044a90') { - return Promise.resolve(null); - } - return Promise.resolve(existingUsername); - }), - updateWhere: jest.fn().mockImplementation((request) => { - if (request.uuid_type.uuid == 'bb281075-1b98-4456-89d6-c643d3044a90') { - return Promise.resolve(newUsernameRequest); - } - if (request.uuid_type.uuid == 'bb281075-1b98-4456-89d6-c643d3044a91') { - return Promise.resolve(updateUsernameRequest); - } - if (request.uuid_type.uuid == 'bb281075-1b98-4456-89d6-c643d3044a92') { - throw new Error('Error'); - } - return Promise.resolve(invalidUpdateUsernameRequest); - }), -}; - -const mockAddUsernameCommand = { - execute: jest.fn().mockResolvedValue(newUsernameRequest), -}; - -const mockMessager = { - publish: jest.fn().mockImplementation(), -}; - -describe('UpdateUsernameUseCase', () => { - let updateUsernameUseCase: UpdateUsernameUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: UsernameRepository, - useValue: mockUsernameRepository, - }, - { - provide: CommandBus, - useValue: mockAddUsernameCommand, - }, - { - provide: Messager, - useValue: mockMessager, - }, - UpdateUsernameUseCase, - UsernameProfile, - ], - }).compile(); - - updateUsernameUseCase = module.get( - UpdateUsernameUseCase, - ); - }); - - it('should be defined', () => { - expect(updateUsernameUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should create a new username', async () => { - const newUsername: Username = await updateUsernameUseCase.execute( - newUsernameCommand, - ); - - expect(newUsername.username).toBe(newUsernameRequest.username); - expect(newUsername.type).toBe(newUsernameRequest.type); - }); - - it('should update a username for email type', async () => { - const updatedUsername: Username = await updateUsernameUseCase.execute( - updateUsernameCommand, - ); - - expect(updatedUsername.username).toBe(updateUsernameRequest.username); - expect(updatedUsername.type).toBe(updateUsernameRequest.type); - }); - - it('should throw an error if username does not exist', async () => { - await expect( - updateUsernameUseCase.execute(unknownUpdateUsernameCommand), - ).rejects.toBeInstanceOf(Error); - }); - - it('should throw an exception if username is invalid', async () => { - await expect( - updateUsernameUseCase.execute(invalidUpdateUsernameCommand), - ).rejects.toBeInstanceOf(BadRequestException); - }); - }); -}); diff --git a/src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts deleted file mode 100644 index 2e75726..0000000 --- a/src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { classes } from '@automapper/classes'; -import { AutomapperModule } from '@automapper/nestjs'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthenticationRepository } from '../../adapters/secondaries/authentication.repository'; -import { Authentication } from '../../domain/entities/authentication'; -import * as bcrypt from 'bcrypt'; -import { ValidateAuthenticationUseCase } from '../../domain/usecases/validate-authentication.usecase'; -import { ValidateAuthenticationQuery } from '../../queries/validate-authentication.query'; -import { UsernameRepository } from '../../adapters/secondaries/username.repository'; -import { Type } from '../../domain/dtos/type.enum'; -import { NotFoundException, UnauthorizedException } from '@nestjs/common'; -import { DatabaseException } from '../../../database/exceptions/database.exception'; -import { ValidateAuthenticationRequest } from '../../domain/dtos/validate-authentication.request'; - -const mockAuthenticationRepository = { - findOne: jest - .fn() - .mockImplementationOnce(() => ({ - uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', - password: bcrypt.hashSync('John123', 10), - })) - .mockImplementationOnce(() => ({ - uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', - password: bcrypt.hashSync('John123', 10), - })), -}; - -const mockUsernameRepository = { - findOne: jest - .fn() - .mockImplementationOnce(() => ({ - uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', - username: 'john.doe@email.com', - type: Type.EMAIL, - })) - .mockImplementationOnce(() => { - throw new DatabaseException(); - }) - .mockImplementationOnce(() => ({ - uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', - username: 'john.doe@email.com', - type: Type.EMAIL, - })), -}; - -describe('ValidateAuthenticationUseCase', () => { - let validateAuthenticationUseCase: ValidateAuthenticationUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })], - providers: [ - { - provide: AuthenticationRepository, - useValue: mockAuthenticationRepository, - }, - { - provide: UsernameRepository, - useValue: mockUsernameRepository, - }, - ValidateAuthenticationUseCase, - ], - }).compile(); - - validateAuthenticationUseCase = module.get( - ValidateAuthenticationUseCase, - ); - }); - - it('should be defined', () => { - expect(validateAuthenticationUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should validate an authentication and returns entity object', async () => { - const validateAuthenticationRequest: ValidateAuthenticationRequest = - new ValidateAuthenticationRequest(); - validateAuthenticationRequest.username = 'john.doe@email.com'; - validateAuthenticationRequest.password = 'John123'; - const authentication: Authentication = - await validateAuthenticationUseCase.execute( - new ValidateAuthenticationQuery( - validateAuthenticationRequest.username, - validateAuthenticationRequest.password, - ), - ); - - expect(authentication.uuid).toBe('bb281075-1b98-4456-89d6-c643d3044a91'); - }); - - it('should not validate an authentication with unknown username and returns not found exception', async () => { - await expect( - validateAuthenticationUseCase.execute( - new ValidateAuthenticationQuery('jane.doe@email.com', 'Jane123'), - ), - ).rejects.toBeInstanceOf(NotFoundException); - }); - - it('should not validate an authentication with wrong password and returns unauthorized exception', async () => { - await expect( - validateAuthenticationUseCase.execute( - new ValidateAuthenticationQuery('john.doe@email.com', 'John1234'), - ), - ).rejects.toBeInstanceOf(UnauthorizedException); - }); - }); -}); diff --git a/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts b/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts index 3f5af01..510e9c4 100644 --- a/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts +++ b/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts @@ -1,6 +1,6 @@ import { ArgumentMetadata } from '@nestjs/common'; -import { ValidateAuthenticationRequest } from '../../../modules/oldauthentication/domain/dtos/validate-authentication.request'; import { RpcValidationPipe } from '../../pipes/rpc.validation-pipe'; +import { ValidateAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/validate-authentication.request.dto'; describe('RpcValidationPipe', () => { it('should not validate request', async () => { @@ -10,11 +10,11 @@ describe('RpcValidationPipe', () => { }); const metadata: ArgumentMetadata = { type: 'body', - metatype: ValidateAuthenticationRequest, + metatype: ValidateAuthenticationRequestDto, data: '', }; await target - .transform({}, metadata) + .transform({}, metadata) .catch((err) => { expect(err.message).toEqual('Rpc Exception'); }); From f111563137811bf282c31dbf942d7e02435e6e83 Mon Sep 17 00:00:00 2001 From: sbriat Date: Wed, 12 Jul 2023 10:41:04 +0200 Subject: [PATCH 18/23] fix symbol --- src/app.di-tokens.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.di-tokens.ts b/src/app.di-tokens.ts index 61203bb..48e8011 100644 --- a/src/app.di-tokens.ts +++ b/src/app.di-tokens.ts @@ -1 +1 @@ -export const MESSAGE_PUBLISHER = Symbol(); +export const MESSAGE_PUBLISHER = Symbol('MESSAGE_PUBLISHER'); From 0ccaafc5e02232dca5db3ca7ddd10ef001632fbc Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 13 Jul 2023 08:58:58 +0200 Subject: [PATCH 19/23] upgrade repositories --- .env.test | 4 + package.json | 2 +- .../delete-username.service.ts | 15 +- .../ports/username.repository.port.ts | 2 + .../infrastructure/username.repository.ts | 16 +- .../delete-username.grpc.controller.ts | 8 +- .../authentication.repository.spec.ts | 231 ++++++++++++++++++ .../integration/username.repository.spec.ts | 202 +++++++++++++++ .../unit/core/delete-username.service.spec.ts | 17 +- .../username.repository.spec.ts | 41 +++- .../delete-username.grpc.controller.spec.ts | 16 ++ 11 files changed, 532 insertions(+), 22 deletions(-) create mode 100644 src/modules/authentication/tests/integration/authentication.repository.spec.ts create mode 100644 src/modules/authentication/tests/integration/username.repository.spec.ts diff --git a/.env.test b/.env.test index bfec28f..9a0151f 100644 --- a/.env.test +++ b/.env.test @@ -5,6 +5,10 @@ SERVICE_PORT=5002 # PRISMA DATABASE_URL="postgresql://mobicoop:mobicoop@localhost:5432/mobicoop-test?schema=auth" +# MESSAGE BROKER +MESSAGE_BROKER_URI=amqp://v3-broker:5672 +MESSAGE_BROKER_EXCHANGE=mobicoop + # OPA OPA_IMAGE=openpolicyagent/opa:0.54.0 OPA_URL=http://v3-auth-opa:8181/v1/data/ diff --git a/package.json b/package.json index 44a6478..4b0c818 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "test": "npm run migrate:test && dotenv -e .env.test jest", "test:unit": "jest --testPathPattern 'tests/unit/' --verbose", "test:unit:ci": "jest --testPathPattern 'tests/unit/' --coverage", - "test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose", + "test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --runInBand", "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/'", "test:cov": "jest --testPathPattern 'tests/unit/' --coverage", "test:e2e": "jest --config ./test/jest-e2e.json", diff --git a/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts b/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts index 5812739..de6439a 100644 --- a/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts +++ b/src/modules/authentication/core/application/commands/delete-username/delete-username.service.ts @@ -1,4 +1,4 @@ -import { Inject } from '@nestjs/common'; +import { Inject, UnauthorizedException } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { DeleteUsernameCommand } from './delete-username.command'; import { USERNAME_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; @@ -13,11 +13,20 @@ export class DeleteUsernameService implements ICommandHandler { ) {} async execute(command: DeleteUsernameCommand): Promise { - const username: UsernameEntity = await this.usernameRepository.findOneById( + const username: UsernameEntity = await this.usernameRepository.findByName( command.name, ); + const usernamesCount: number = await this.usernameRepository.countUsernames( + username.getProps().userId, + ); + if (usernamesCount <= 1) + throw new UnauthorizedException( + 'Authentication must have at least one username', + ); username.delete(); - const isDeleted: boolean = await this.usernameRepository.delete(username); + const isDeleted: boolean = await this.usernameRepository.deleteUsername( + username, + ); return isDeleted; } } diff --git a/src/modules/authentication/core/application/ports/username.repository.port.ts b/src/modules/authentication/core/application/ports/username.repository.port.ts index 9188472..4ce4084 100644 --- a/src/modules/authentication/core/application/ports/username.repository.port.ts +++ b/src/modules/authentication/core/application/ports/username.repository.port.ts @@ -6,4 +6,6 @@ export type UsernameRepositoryPort = RepositoryPort & { findByType(userId: string, type: Type): Promise; findByName(name: string): Promise; updateUsername(oldName: string, entity: UsernameEntity): Promise; + deleteUsername(entity: UsernameEntity): Promise; + countUsernames(userId: string): Promise; }; diff --git a/src/modules/authentication/infrastructure/username.repository.ts b/src/modules/authentication/infrastructure/username.repository.ts index 095962a..6b06d0d 100644 --- a/src/modules/authentication/infrastructure/username.repository.ts +++ b/src/modules/authentication/infrastructure/username.repository.ts @@ -71,11 +71,13 @@ export class UsernameRepository updateUsername = async ( oldName: string, entity: UsernameEntity, - ): Promise => - this.updateWhere( - { - username: oldName, - }, - entity, - ); + ): Promise => this.update(oldName, entity, 'username'); + + deleteUsername = async (entity: UsernameEntity): Promise => + this.delete(entity, 'username'); + + countUsernames = async (userId: string): Promise => + this.count({ + authUuid: userId, + }); } diff --git a/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts index 389c78a..6010a62 100644 --- a/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts +++ b/src/modules/authentication/interface/grpc-controllers/delete-username.grpc.controller.ts @@ -4,7 +4,7 @@ import { RpcExceptionCode, RpcValidationPipe, } from '@mobicoop/ddd-library'; -import { Controller, UsePipes } from '@nestjs/common'; +import { Controller, UnauthorizedException, UsePipes } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { DeleteUsernameRequestDto } from './dtos/delete-username.request.dto'; @@ -25,12 +25,16 @@ export class DeleteUsernameGrpcController { try { await this.commandBus.execute(new DeleteUsernameCommand(data)); } catch (error: any) { + if (error instanceof UnauthorizedException) + throw new RpcException({ + code: RpcExceptionCode.PERMISSION_DENIED, + message: error.message, + }); if (error instanceof NotFoundException) throw new RpcException({ code: RpcExceptionCode.NOT_FOUND, message: error.message, }); - if (error instanceof DatabaseErrorException) throw new RpcException({ code: RpcExceptionCode.INTERNAL, diff --git a/src/modules/authentication/tests/integration/authentication.repository.spec.ts b/src/modules/authentication/tests/integration/authentication.repository.spec.ts new file mode 100644 index 0000000..e435adf --- /dev/null +++ b/src/modules/authentication/tests/integration/authentication.repository.spec.ts @@ -0,0 +1,231 @@ +import { TestingModule, Test } from '@nestjs/testing'; +import { v4 } from 'uuid'; +import * as bcrypt from 'bcrypt'; +import { PrismaService } from '@modules/authentication/infrastructure/prisma.service'; +import { AuthenticationRepository } from '@modules/authentication/infrastructure/authentication.repository'; +import { AuthenticationMapper } from '@modules/authentication/authentication.mapper'; +import { EventEmitterModule } from '@nestjs/event-emitter'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { + DatabaseErrorException, + NotFoundException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { CreateAuthenticationProps } from '@modules/authentication/core/domain/authentication.types'; + +const uuid = '165192d4-398a-4469-a16b-98c02cc6f531'; + +const createAuthenticationProps: CreateAuthenticationProps = { + userId: uuid, + password: 'somePassword', + usernames: [ + { + type: Type.EMAIL, + name: 'john.doe@email.com', + }, + ], +}; + +const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), +}; + +const mockLogger = { + log: jest.fn(), + warn: jest.fn(), + error: jest.fn(), +}; + +describe('AuthenticationRepository', () => { + let prismaService: PrismaService; + let authenticationRepository: AuthenticationRepository; + + // const createAuthentications = async (nbToCreate = 10) => { + // for (let i = 0; i < nbToCreate; i++) { + // await prismaService.auth.create({ + // data: { + // uuid: v4(), + // password: bcrypt.hashSync(`password-${i}`, 10), + // }, + // }); + // } + // }; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [EventEmitterModule.forRoot()], + providers: [ + AuthenticationRepository, + PrismaService, + AuthenticationMapper, + { + provide: MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, + }, + ], + }) + // disable logging + .setLogger(mockLogger) + .compile(); + + prismaService = module.get(PrismaService); + authenticationRepository = module.get( + AuthenticationRepository, + ); + }); + + afterAll(async () => { + await prismaService.$disconnect(); + }); + + beforeEach(async () => { + await prismaService.auth.deleteMany(); + }); + + // describe('findAll', () => { + // it('should return an empty data array', async () => { + // const res = await authenticationRepository.findAll(); + // expect(res).toEqual({ + // data: [], + // total: 0, + // }); + // }); + + // it('should return a data array with 8 auths', async () => { + // await createAuthentications(8); + // const auths = await authenticationRepository.findAll(); + // expect(auths.data.length).toBe(8); + // expect(auths.total).toBe(8); + // }); + + // it('should return a data array limited to 10 authentications', async () => { + // await createAuthentications(20); + // const auths = await authenticationRepository.findAll(); + // expect(auths.data.length).toBe(10); + // expect(auths.total).toBe(20); + // }); + // }); + + describe('findOneById', () => { + it('should return an authentication', async () => { + const authToFind = await prismaService.auth.create({ + data: { + uuid: v4(), + password: bcrypt.hashSync(`password`, 10), + }, + }); + + const auth = await authenticationRepository.findOneById(authToFind.uuid); + expect(auth.id).toBe(authToFind.uuid); + }); + + it('should throw an exception if record is not found', async () => { + await expect( + authenticationRepository.findOneById( + '544572be-11fb-4244-8235-587221fc9104', + ), + ).rejects.toBeInstanceOf(NotFoundException); + }); + }); + + describe('create', () => { + it('should create an authentication', async () => { + const beforeCount = await prismaService.auth.count(); + + const authenticationToCreate: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + await authenticationRepository.insert(authenticationToCreate); + + const afterCount = await prismaService.auth.count(); + + expect(afterCount - beforeCount).toBe(1); + }); + + it('should throw a UniqueConstraintException if authentication already exists', async () => { + await prismaService.auth.create({ + data: { + uuid: uuid, + password: bcrypt.hashSync(`password`, 10), + }, + }); + + const authenticationToCreate: AuthenticationEntity = + await AuthenticationEntity.create(createAuthenticationProps); + await expect( + authenticationRepository.insert(authenticationToCreate), + ).rejects.toBeInstanceOf(UniqueConstraintException); + }); + }); + + describe('update', () => { + it('should update an authentication', async () => { + const authenticationToUpdate = await prismaService.auth.create({ + data: { + uuid: v4(), + password: bcrypt.hashSync(`password`, 10), + }, + }); + + const toUpdate: AuthenticationEntity = await AuthenticationEntity.create( + createAuthenticationProps, + ); + await authenticationRepository.update( + authenticationToUpdate.uuid, + toUpdate, + ); + + const updatedAuthentication = await prismaService.auth.findUnique({ + where: { + uuid: toUpdate.id, + }, + }); + + expect(updatedAuthentication.uuid).toBe(uuid); + expect(authenticationToUpdate.updatedAt.getTime()).toBeLessThan( + updatedAuthentication.updatedAt.getTime(), + ); + }); + + it('should throw a DatabaseException if id is unknown', async () => { + const toUpdate: AuthenticationEntity = await AuthenticationEntity.create( + createAuthenticationProps, + ); + + await expect( + authenticationRepository.update( + '544572be-11fb-4244-8235-587221fc9104', + toUpdate, + ), + ).rejects.toBeInstanceOf(DatabaseErrorException); + }); + }); + + describe('delete', () => { + it('should delete an authentication', async () => { + await prismaService.auth.create({ + data: { + uuid, + password: bcrypt.hashSync(`password`, 10), + }, + }); + const toDelete: AuthenticationEntity = await AuthenticationEntity.create( + createAuthenticationProps, + ); + await authenticationRepository.delete(toDelete); + + const count = await prismaService.auth.count(); + expect(count).toBe(0); + }); + + it('should throw a DatabaseException if authentication does not exist', async () => { + const toDelete: AuthenticationEntity = await AuthenticationEntity.create( + createAuthenticationProps, + ); + await expect( + authenticationRepository.delete(toDelete), + ).rejects.toBeInstanceOf(DatabaseErrorException); + }); + }); +}); diff --git a/src/modules/authentication/tests/integration/username.repository.spec.ts b/src/modules/authentication/tests/integration/username.repository.spec.ts new file mode 100644 index 0000000..55ea75a --- /dev/null +++ b/src/modules/authentication/tests/integration/username.repository.spec.ts @@ -0,0 +1,202 @@ +import { TestingModule, Test } from '@nestjs/testing'; +import { PrismaService } from '@modules/authentication/infrastructure/prisma.service'; +import { EventEmitterModule } from '@nestjs/event-emitter'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { + DatabaseErrorException, + NotFoundException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { + CreateUsernameProps, + Type, +} from '@modules/authentication/core/domain/username.types'; +import { UsernameRepository } from '@modules/authentication/infrastructure/username.repository'; +import { UsernameMapper } from '@modules/authentication/username.mapper'; +import * as bcrypt from 'bcrypt'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; + +const authUuid = 'a4524d22-7be3-46cd-8444-3145470476dc'; + +const createUsernameProps: CreateUsernameProps = { + userId: authUuid, + type: Type.EMAIL, + name: 'john.doe@email.com', +}; + +const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), +}; + +const mockLogger = { + log: jest.fn(), + warn: jest.fn(), + error: jest.fn(), +}; + +describe('UsernameRepository', () => { + let prismaService: PrismaService; + let usernameRepository: UsernameRepository; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [EventEmitterModule.forRoot()], + providers: [ + UsernameRepository, + PrismaService, + UsernameMapper, + { + provide: MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, + }, + ], + }) + // disable logging + .setLogger(mockLogger) + .compile(); + + prismaService = module.get(PrismaService); + usernameRepository = module.get(UsernameRepository); + }); + + afterAll(async () => { + await prismaService.$disconnect(); + }); + + beforeEach(async () => { + await prismaService.username.deleteMany(); + await prismaService.auth.deleteMany(); + await prismaService.auth.create({ + data: { + uuid: authUuid, + password: bcrypt.hashSync(`password`, 10), + }, + }); + }); + + describe('findOne', () => { + it('should return a Username', async () => { + await prismaService.username.create({ + data: { + authUuid, + username: 'john.doe@email.com', + type: Type.EMAIL, + }, + }); + + const username = await usernameRepository.findOne({ + username: 'john.doe@email.com', + }); + expect(username.id).toBe('john.doe@email.com'); + }); + + it('should throw an exception if record is not found', async () => { + await expect( + usernameRepository.findOne({ + username: 'jane.doe@email.com', + }), + ).rejects.toBeInstanceOf(NotFoundException); + }); + }); + + describe('create', () => { + it('should create a username', async () => { + const beforeCount = await prismaService.username.count(); + + const usernameToCreate: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + await usernameRepository.insert(usernameToCreate); + + const afterCount = await prismaService.username.count(); + + expect(afterCount - beforeCount).toBe(1); + }); + + it('should throw a UniqueConstraintException if username already exists', async () => { + await prismaService.username.create({ + data: { + authUuid, + type: Type.EMAIL, + username: 'john.doe@email.com', + }, + }); + + const usernameToCreate: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + await expect( + usernameRepository.insert(usernameToCreate), + ).rejects.toBeInstanceOf(UniqueConstraintException); + }); + }); + + describe('update username', () => { + it('should update the name of a username', async () => { + const usernameToUpdate = await prismaService.username.create({ + data: { + authUuid, + type: Type.EMAIL, + username: 'johnny.doe@email.com', + }, + }); + + const toUpdate: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + await usernameRepository.updateUsername( + usernameToUpdate.username, + toUpdate, + ); + + const updatedUsername = await prismaService.username.findUnique({ + where: { + username: toUpdate.id, + }, + }); + + expect(updatedUsername.username).toBe('john.doe@email.com'); + expect(usernameToUpdate.updatedAt.getTime()).toBeLessThan( + updatedUsername.updatedAt.getTime(), + ); + }); + + it('should throw a DatabaseException if id is unknown', async () => { + const toUpdate: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + + await expect( + usernameRepository.updateUsername('jane.doe@email.com', toUpdate), + ).rejects.toBeInstanceOf(DatabaseErrorException); + }); + }); + + describe('delete', () => { + it('should delete a username', async () => { + await prismaService.username.create({ + data: { + authUuid, + type: Type.EMAIL, + username: 'john.doe@email.com', + }, + }); + const toDelete: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + await usernameRepository.delete(toDelete); + + const count = await prismaService.username.count(); + expect(count).toBe(0); + }); + + it('should throw a DatabaseException if username does not exist', async () => { + const toDelete: UsernameEntity = await UsernameEntity.create( + createUsernameProps, + ); + await expect(usernameRepository.delete(toDelete)).rejects.toBeInstanceOf( + DatabaseErrorException, + ); + }); + }); +}); diff --git a/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts b/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts index 57d22e7..12a5cec 100644 --- a/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts +++ b/src/modules/authentication/tests/unit/core/delete-username.service.spec.ts @@ -2,6 +2,7 @@ import { USERNAME_REPOSITORY } from '@modules/authentication/authentication.di-t import { DeleteUsernameCommand } from '@modules/authentication/core/application/commands/delete-username/delete-username.command'; import { DeleteUsernameService } from '@modules/authentication/core/application/commands/delete-username/delete-username.service'; import { DeleteUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto'; +import { UnauthorizedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; const deleteUsernameRequest: DeleteUsernameRequestDto = { @@ -10,11 +11,18 @@ const deleteUsernameRequest: DeleteUsernameRequestDto = { const mockUsernameEntity = { delete: jest.fn(), + getProps: jest.fn().mockImplementation(() => ({ + userId: 'b1643072-757f-4e3b-87f2-63af3684afb8', + })), }; const mockUsernameRepository = { - findOneById: jest.fn().mockImplementation(() => mockUsernameEntity), - delete: jest.fn().mockImplementationOnce(() => true), + findByName: jest.fn().mockImplementation(() => mockUsernameEntity), + countUsernames: jest + .fn() + .mockImplementationOnce(() => 2) + .mockImplementationOnce(() => 1), + deleteUsername: jest.fn().mockImplementationOnce(() => true), }; describe('Delete Username Service', () => { @@ -50,5 +58,10 @@ describe('Delete Username Service', () => { ); expect(result).toBeTruthy(); }); + it('should throw an exception when trying to delete the last username', async () => { + await expect( + deleteUsernameService.execute(deleteUsernameCommand), + ).rejects.toBeInstanceOf(UnauthorizedException); + }); }); }); diff --git a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts index 7a58e72..88fe27b 100644 --- a/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts +++ b/src/modules/authentication/tests/unit/infrastructure/username.repository.spec.ts @@ -22,8 +22,9 @@ const mockPrismaService = { }; return record; }), - update: jest.fn().mockImplementation(), + delete: jest.fn().mockImplementation(), + count: jest.fn().mockImplementation(), }, }; @@ -67,6 +68,7 @@ describe('Username repository', () => { it('should be defined', () => { expect(usernameRepository).toBeDefined(); }); + it('should find a username by its userId and Type', async () => { jest.spyOn(usernameRepository, 'findOne'); const username: UsernameEntity = await usernameRepository.findByType( @@ -80,6 +82,7 @@ describe('Username repository', () => { }); expect(username.getProps().name).toBe('john.doe@email.com'); }); + it('should find a username by its name', async () => { jest.spyOn(usernameRepository, 'findOne'); const username: UsernameEntity = await usernameRepository.findByName( @@ -91,8 +94,9 @@ describe('Username repository', () => { }); expect(username.getProps().name).toBe('john.doe@email.com'); }); + it('should update a username', async () => { - jest.spyOn(usernameRepository, 'updateWhere'); + jest.spyOn(usernameRepository, 'update'); const usernameToUpdate: UsernameEntity = await UsernameEntity.create({ userId: '165192d4-398a-4469-a16b-98c02cc6f531', type: Type.EMAIL, @@ -102,12 +106,35 @@ describe('Username repository', () => { 'john.doe@email.com', usernameToUpdate, ); - expect(usernameRepository.updateWhere).toHaveBeenCalledTimes(1); - expect(usernameRepository.updateWhere).toHaveBeenCalledWith( - { - username: 'john.doe@email.com', - }, + expect(usernameRepository.update).toHaveBeenCalledTimes(1); + expect(usernameRepository.update).toHaveBeenCalledWith( + 'john.doe@email.com', usernameToUpdate, + 'username', ); }); + + it('should delete a username', async () => { + jest.spyOn(usernameRepository, 'delete'); + const usernameToDelete: UsernameEntity = await UsernameEntity.create({ + userId: '165192d4-398a-4469-a16b-98c02cc6f531', + type: Type.EMAIL, + name: 'john.doe@new-email.com', + }); + await usernameRepository.deleteUsername(usernameToDelete); + expect(usernameRepository.delete).toHaveBeenCalledTimes(1); + expect(usernameRepository.delete).toHaveBeenCalledWith( + usernameToDelete, + 'username', + ); + }); + + it('should count usernames for a given userId', async () => { + jest.spyOn(usernameRepository, 'count'); + await usernameRepository.countUsernames('john.doe@email.com'); + expect(usernameRepository.count).toHaveBeenCalledTimes(1); + expect(usernameRepository.count).toHaveBeenCalledWith({ + authUuid: 'john.doe@email.com', + }); + }); }); diff --git a/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts index c4e795c..3bb2ca8 100644 --- a/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts +++ b/src/modules/authentication/tests/unit/interface/delete-username.grpc.controller.spec.ts @@ -5,6 +5,7 @@ import { import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { DeleteUsernameGrpcController } from '@modules/authentication/interface/grpc-controllers/delete-username.grpc.controller'; import { DeleteUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-username.request.dto'; +import { UnauthorizedException } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { RpcException } from '@nestjs/microservices'; import { Test, TestingModule } from '@nestjs/testing'; @@ -17,6 +18,9 @@ const mockCommandBus = { execute: jest .fn() .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new UnauthorizedException(); + }) .mockImplementationOnce(() => { throw new NotFoundException(); }) @@ -61,6 +65,18 @@ describe('Delete Username Grpc Controller', () => { expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); }); + it('should throw a dedicated RpcException when trying to delete the last username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteUsernameGrpcController.delete(deleteUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.PERMISSION_DENIED); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + it('should throw a dedicated RpcException if username does not exist', async () => { jest.spyOn(mockCommandBus, 'execute'); expect.assertions(3); From c3f31c4d2a94d581797106f5e23ded031d0a12d7 Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 13 Jul 2023 09:04:54 +0200 Subject: [PATCH 20/23] repositories integration tests --- .../tests/integration/username.repository.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/authentication/tests/integration/username.repository.spec.ts b/src/modules/authentication/tests/integration/username.repository.spec.ts index 55ea75a..18eb06a 100644 --- a/src/modules/authentication/tests/integration/username.repository.spec.ts +++ b/src/modules/authentication/tests/integration/username.repository.spec.ts @@ -74,7 +74,7 @@ describe('UsernameRepository', () => { }); }); - describe('findOne', () => { + describe('findByName', () => { it('should return a Username', async () => { await prismaService.username.create({ data: { @@ -84,9 +84,9 @@ describe('UsernameRepository', () => { }, }); - const username = await usernameRepository.findOne({ - username: 'john.doe@email.com', - }); + const username = await usernameRepository.findByName( + 'john.doe@email.com', + ); expect(username.id).toBe('john.doe@email.com'); }); @@ -184,7 +184,7 @@ describe('UsernameRepository', () => { const toDelete: UsernameEntity = await UsernameEntity.create( createUsernameProps, ); - await usernameRepository.delete(toDelete); + await usernameRepository.deleteUsername(toDelete); const count = await prismaService.username.count(); expect(count).toBe(0); From 90d5bac4978d92c3e98740293fae97074fb16bac Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 13 Jul 2023 10:12:55 +0200 Subject: [PATCH 21/23] update package.json --- package-lock.json | 8 ++++---- package.json | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 77683c0..7d89135 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@golevelup/nestjs-rabbitmq": "^3.4.0", "@grpc/grpc-js": "^1.8.0", "@grpc/proto-loader": "^0.7.4", - "@mobicoop/ddd-library": "file:../../packages/dddlibrary", + "@mobicoop/ddd-library": "^0.3.0", "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.0.0", @@ -1492,9 +1492,9 @@ } }, "node_modules/@mobicoop/ddd-library": { - "version": "0.2.0", - "resolved": "file:../../packages/dddlibrary", - "license": "AGPL", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-0.3.0.tgz", + "integrity": "sha512-MoUDqlrDmJkumCFSyW9FY2DLbguT4rytFrmBt9tVNCr2Es6nlz4Ml3HVBwJTZrlJFU79XmiUQ5WAO0MHJt+nAg==", "dependencies": { "@nestjs/event-emitter": "^1.4.2", "@nestjs/microservices": "^9.4.0", diff --git a/package.json b/package.json index 4b0c818..60ddccf 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,11 @@ "lint:check": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix-dry-run --ignore-path .gitignore", "pretty:check": "./node_modules/.bin/prettier --check .", "pretty": "./node_modules/.bin/prettier --write .", - "test": "npm run migrate:test && dotenv -e .env.test jest", + "test": "npm run test:unit && npm run test:integration", "test:unit": "jest --testPathPattern 'tests/unit/' --verbose", "test:unit:ci": "jest --testPathPattern 'tests/unit/' --coverage", "test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --runInBand", - "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/'", + "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand", "test:cov": "jest --testPathPattern 'tests/unit/' --coverage", "test:e2e": "jest --config ./test/jest-e2e.json", "migrate": "docker exec v3-auth-api sh -c 'npx prisma migrate dev'", @@ -36,7 +36,7 @@ "@golevelup/nestjs-rabbitmq": "^3.4.0", "@grpc/grpc-js": "^1.8.0", "@grpc/proto-loader": "^0.7.4", - "@mobicoop/ddd-library": "file:../../packages/dddlibrary", + "@mobicoop/ddd-library": "^0.3.0", "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.0.0", From 5cccb8252b5d357bb8076258863719c934bba064 Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 13 Jul 2023 10:13:06 +0200 Subject: [PATCH 22/23] 0.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7d89135..b8406bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mobicoop/auth", - "version": "0.0.1", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mobicoop/auth", - "version": "0.0.1", + "version": "0.1.0", "license": "AGPL", "dependencies": { "@automapper/classes": "^8.7.7", diff --git a/package.json b/package.json index 60ddccf..efcee6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mobicoop/auth", - "version": "0.0.1", + "version": "0.1.0", "description": "Mobicoop V3 Auth Service", "author": "sbriat", "private": true, From 82601ff780827cb572c70c2c3286b15f4dee2292 Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 13 Jul 2023 10:17:38 +0200 Subject: [PATCH 23/23] update readme --- README.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ad221ff..cc7bc53 100644 --- a/README.md +++ b/README.md @@ -59,14 +59,18 @@ Note that all usernames are unique in the system : many users can't have the sam For AuthN, the app exposes the following [gRPC](https://grpc.io/) services : -- **Create** : create an auth with one username / password (you can't create multiple usernames at once) +- **Create** : create an auth with usernames and a password ```json { - "uuid": "30f49838-3f24-42bb-a489-8ffb480173ae", - "username": "john.doe@email.com", - "password": "John123", - "type": "EMAIL" + "userId": "30f49838-3f24-42bb-a489-8ffb480173ae", + "usernames": [ + { + "name": "john.doe@email.com", + "type": "EMAIL" + } + ], + "password": "John123" } ``` @@ -74,8 +78,8 @@ For AuthN, the app exposes the following [gRPC](https://grpc.io/) services : ```json { - "uuid": "30f49838-3f24-42bb-a489-8ffb480173ae", - "username": "+33611223344", + "userId": "30f49838-3f24-42bb-a489-8ffb480173ae", + "name": "+33611223344", "type": "PHONE" } ``` @@ -84,8 +88,8 @@ For AuthN, the app exposes the following [gRPC](https://grpc.io/) services : ```json { - "uuid": "30f49838-3f24-42bb-a489-8ffb480173ae", - "username": "johnny.doe@email.com", + "userId": "30f49838-3f24-42bb-a489-8ffb480173ae", + "name": "johnny.doe@email.com", "type": "EMAIL" } ``` @@ -94,7 +98,7 @@ For AuthN, the app exposes the following [gRPC](https://grpc.io/) services : ```json { - "username": "+33611223344" + "name": "+33611223344" } ``` @@ -102,16 +106,16 @@ For AuthN, the app exposes the following [gRPC](https://grpc.io/) services : ```json { - "uuid": "30f49838-3f24-42bb-a489-8ffb480173ae", + "userId": "30f49838-3f24-42bb-a489-8ffb480173ae", "password": "Johnny123" } ``` -- **Validate** : validate an auth (= authentication with username/password) +- **Validate** : validate an auth (= authentication with name/password) ```json { - "username": "john.doe@email.com", + "name": "john.doe@email.com", "password": "Johnny123" } ``` @@ -120,7 +124,7 @@ For AuthN, the app exposes the following [gRPC](https://grpc.io/) services : ```json { - "uuid": "30f49838-3f24-42bb-a489-8ffb480173ae" + "userId": "30f49838-3f24-42bb-a489-8ffb480173ae" } ``` @@ -134,7 +138,7 @@ For AuthZ, the app exposes the following [gRPC](https://grpc.io/) services : ```json { - "uuid": "96d99d44-e0a6-458e-a656-de2a400d60a9", + "userId": "96d99d44-e0a6-458e-a656-de2a400d60a9", "domain": "USER", "action": "READ", "context": [