diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..23435fa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +test +.vscode +.git +.github +.yarn/sdks +node_modules diff --git a/.eslintrc.js b/.eslintrc.js index 653da09..496cf75 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,6 +28,7 @@ module.exports = { "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], "import/order": [ "error", { @@ -37,7 +38,7 @@ module.exports = { { pattern: "@nestjs/**", group: "internal", - position: "after", + position: "before", }, { pattern: "@/**", @@ -51,5 +52,6 @@ module.exports = { }, }, ], + "@typescript-eslint/no-unused-vars": ["error", { varsIgnorePattern: "_" }], }, }; diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..f37084b --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,60 @@ +name: SEOKO Server Deploy + +on: + push: + branches: ["main"] + +jobs: + build: + name: Build and Push Project + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node.js environment + uses: actions/setup-node@v3.5.1 + with: + node-version: 16 + + - name: Install the project dependencies + run: yarn install + + - name: Create .env file + run: | + jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' <<< "$SECRETS_CONTEXT" > .env + cat .env + env: + SECRETS_CONTEXT: ${{ toJson(secrets) }} + + - name: Login to Docker Registry + uses: docker/login-action@v2 + with: + registry: ${{secrets.DOCKER_REGISTRY_URL}} + username: ${{secrets.DOCKER_REGISTRY_ACCESS_KEY}} + password: ${{secrets.DOCKER_REGISTRY_SECRET_KEY}} + + - name: Docker build & push to push + run: docker buildx build -t ${{secrets.DOCKER_REGISTRY_URL}}/${{secrets.BACK_DOCKER_IMAGE_NAME}}:${{ github.sha }} -t ${{secrets.DOCKER_REGISTRY_URL}}/${{secrets.BACK_DOCKER_IMAGE_NAME}}:latest --push . + working-directory: ${{ env.working-directory }} + + deploy: + needs: build + name: Deploy + runs-on: [seoko-server] + steps: + - name: Login to Docker Registry + uses: docker/login-action@v2 + with: + registry: ${{secrets.DOCKER_REGISTRY_URL}} + username: ${{secrets.DOCKER_REGISTRY_ACCESS_KEY}} + password: ${{secrets.DOCKER_REGISTRY_SECRET_KEY}} + + - name: Docker run + run: | + if [ `docker ps -f "name=back" -q` ] + then + docker rm -f $(docker ps -f "name=back" -q) + fi + docker pull ${{secrets.DOCKER_REGISTRY_URL}}/${{secrets.BACK_DOCKER_IMAGE_NAME}}:${{ github.sha }} + docker run -dit -p 3065:3065 --name back ${{secrets.DOCKER_REGISTRY_URL}}/${{secrets.BACK_DOCKER_IMAGE_NAME}}:${{ github.sha }} diff --git a/.pnp.cjs b/.pnp.cjs index 486e271..8546435 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -28,24 +28,42 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { [null, {\ "packageLocation": "./",\ "packageDependencies": [\ + ["@nestjs/axios", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0"],\ ["@nestjs/cli", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.8"],\ ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/config", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0"],\ ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/jwt", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0"],\ + ["@nestjs/mapped-types", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:2.0.2"],\ + ["@nestjs/mongoose", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0"],\ + ["@nestjs/passport", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0"],\ ["@nestjs/platform-express", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ ["@nestjs/schematics", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.1"],\ - ["@nestjs/testing", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/testing", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0"],\ + ["@types/cookie-parser", "npm:1.4.3"],\ ["@types/express", "npm:4.17.17"],\ ["@types/jest", "npm:29.5.3"],\ + ["@types/multer", "npm:1.4.7"],\ ["@types/node", "npm:20.4.2"],\ ["@types/supertest", "npm:2.0.12"],\ ["@typescript-eslint/eslint-plugin", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:5.62.0"],\ ["@typescript-eslint/parser", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:5.62.0"],\ + ["bcryptjs", "npm:2.4.3"],\ + ["class-transformer", "npm:0.5.1"],\ + ["class-validator", "npm:0.14.0"],\ + ["cookie-parser", "npm:1.4.6"],\ + ["crypto-js", "npm:4.1.1"],\ ["eslint", "npm:8.45.0"],\ ["eslint-config-prettier", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:8.8.0"],\ ["eslint-import-resolver-typescript", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.5.5"],\ ["eslint-plugin-import", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:2.27.5"],\ ["eslint-plugin-prettier", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:4.2.1"],\ ["jest", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:29.6.1"],\ + ["mongodb-memory-server", "npm:8.13.0"],\ + ["mongoose", "npm:7.3.4"],\ + ["multer", "npm:1.4.5-lts.1"],\ + ["passport-jwt", "npm:4.0.1"],\ + ["passport-local", "npm:1.0.0"],\ ["prettier", "npm:2.8.8"],\ ["reflect-metadata", "npm:0.1.13"],\ ["rxjs", "npm:7.8.1"],\ @@ -191,6 +209,564 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@aws-crypto/crc32", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@aws-crypto-crc32-npm-3.0.0-10d83e85b0-9fdb3e837f.zip/node_modules/@aws-crypto/crc32/",\ + "packageDependencies": [\ + ["@aws-crypto/crc32", "npm:3.0.0"],\ + ["@aws-crypto/util", "npm:3.0.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["tslib", "npm:1.14.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-crypto/ie11-detection", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@aws-crypto-ie11-detection-npm-3.0.0-71f24dcf6a-299b2ddd46.zip/node_modules/@aws-crypto/ie11-detection/",\ + "packageDependencies": [\ + ["@aws-crypto/ie11-detection", "npm:3.0.0"],\ + ["tslib", "npm:1.14.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-crypto/sha256-browser", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@aws-crypto-sha256-browser-npm-3.0.0-467f48a447-ca89456bf5.zip/node_modules/@aws-crypto/sha256-browser/",\ + "packageDependencies": [\ + ["@aws-crypto/sha256-browser", "npm:3.0.0"],\ + ["@aws-crypto/ie11-detection", "npm:3.0.0"],\ + ["@aws-crypto/sha256-js", "npm:3.0.0"],\ + ["@aws-crypto/supports-web-crypto", "npm:3.0.0"],\ + ["@aws-crypto/util", "npm:3.0.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-locate-window", "npm:3.310.0"],\ + ["@aws-sdk/util-utf8-browser", "npm:3.259.0"],\ + ["tslib", "npm:1.14.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-crypto/sha256-js", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@aws-crypto-sha256-js-npm-3.0.0-2ba1013fd6-644ded32ea.zip/node_modules/@aws-crypto/sha256-js/",\ + "packageDependencies": [\ + ["@aws-crypto/sha256-js", "npm:3.0.0"],\ + ["@aws-crypto/util", "npm:3.0.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["tslib", "npm:1.14.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-crypto/supports-web-crypto", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@aws-crypto-supports-web-crypto-npm-3.0.0-55222d294a-35479a1558.zip/node_modules/@aws-crypto/supports-web-crypto/",\ + "packageDependencies": [\ + ["@aws-crypto/supports-web-crypto", "npm:3.0.0"],\ + ["tslib", "npm:1.14.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-crypto/util", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@aws-crypto-util-npm-3.0.0-6c4b38c78e-d29d554504.zip/node_modules/@aws-crypto/util/",\ + "packageDependencies": [\ + ["@aws-crypto/util", "npm:3.0.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-utf8-browser", "npm:3.259.0"],\ + ["tslib", "npm:1.14.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/client-cognito-identity", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-client-cognito-identity-npm-3.370.0-9b45c43400-e13eb18f70.zip/node_modules/@aws-sdk/client-cognito-identity/",\ + "packageDependencies": [\ + ["@aws-sdk/client-cognito-identity", "npm:3.370.0"],\ + ["@aws-crypto/sha256-browser", "npm:3.0.0"],\ + ["@aws-crypto/sha256-js", "npm:3.0.0"],\ + ["@aws-sdk/client-sts", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-node", "npm:3.370.0"],\ + ["@aws-sdk/middleware-host-header", "npm:3.370.0"],\ + ["@aws-sdk/middleware-logger", "npm:3.370.0"],\ + ["@aws-sdk/middleware-recursion-detection", "npm:3.370.0"],\ + ["@aws-sdk/middleware-signing", "npm:3.370.0"],\ + ["@aws-sdk/middleware-user-agent", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-endpoints", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-browser", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-node", "virtual:a9bd886ca27f871d002c65d71be20bce00ad3beae5ce61ef3b91b8088aeb133e0e165a852d092a26f89cb4a09b3ba4179aa731ed3340041afca9f58e099525b3#npm:3.370.0"],\ + ["@smithy/config-resolver", "npm:1.0.2"],\ + ["@smithy/fetch-http-handler", "npm:1.0.2"],\ + ["@smithy/hash-node", "npm:1.0.2"],\ + ["@smithy/invalid-dependency", "npm:1.0.2"],\ + ["@smithy/middleware-content-length", "npm:1.0.2"],\ + ["@smithy/middleware-endpoint", "npm:1.0.3"],\ + ["@smithy/middleware-retry", "npm:1.0.4"],\ + ["@smithy/middleware-serde", "npm:1.0.2"],\ + ["@smithy/middleware-stack", "npm:1.0.2"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/node-http-handler", "npm:1.0.3"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/smithy-client", "npm:1.0.4"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["@smithy/util-body-length-browser", "npm:1.0.2"],\ + ["@smithy/util-body-length-node", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-browser", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-node", "npm:1.0.2"],\ + ["@smithy/util-retry", "npm:1.0.4"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/client-sso", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-client-sso-npm-3.370.0-a9bd886ca2-e6797cac37.zip/node_modules/@aws-sdk/client-sso/",\ + "packageDependencies": [\ + ["@aws-sdk/client-sso", "npm:3.370.0"],\ + ["@aws-crypto/sha256-browser", "npm:3.0.0"],\ + ["@aws-crypto/sha256-js", "npm:3.0.0"],\ + ["@aws-sdk/middleware-host-header", "npm:3.370.0"],\ + ["@aws-sdk/middleware-logger", "npm:3.370.0"],\ + ["@aws-sdk/middleware-recursion-detection", "npm:3.370.0"],\ + ["@aws-sdk/middleware-user-agent", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-endpoints", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-browser", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-node", "virtual:a9bd886ca27f871d002c65d71be20bce00ad3beae5ce61ef3b91b8088aeb133e0e165a852d092a26f89cb4a09b3ba4179aa731ed3340041afca9f58e099525b3#npm:3.370.0"],\ + ["@smithy/config-resolver", "npm:1.0.2"],\ + ["@smithy/fetch-http-handler", "npm:1.0.2"],\ + ["@smithy/hash-node", "npm:1.0.2"],\ + ["@smithy/invalid-dependency", "npm:1.0.2"],\ + ["@smithy/middleware-content-length", "npm:1.0.2"],\ + ["@smithy/middleware-endpoint", "npm:1.0.3"],\ + ["@smithy/middleware-retry", "npm:1.0.4"],\ + ["@smithy/middleware-serde", "npm:1.0.2"],\ + ["@smithy/middleware-stack", "npm:1.0.2"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/node-http-handler", "npm:1.0.3"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/smithy-client", "npm:1.0.4"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["@smithy/util-body-length-browser", "npm:1.0.2"],\ + ["@smithy/util-body-length-node", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-browser", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-node", "npm:1.0.2"],\ + ["@smithy/util-retry", "npm:1.0.4"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/client-sso-oidc", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-client-sso-oidc-npm-3.370.0-9943f04319-a94d58fdef.zip/node_modules/@aws-sdk/client-sso-oidc/",\ + "packageDependencies": [\ + ["@aws-sdk/client-sso-oidc", "npm:3.370.0"],\ + ["@aws-crypto/sha256-browser", "npm:3.0.0"],\ + ["@aws-crypto/sha256-js", "npm:3.0.0"],\ + ["@aws-sdk/middleware-host-header", "npm:3.370.0"],\ + ["@aws-sdk/middleware-logger", "npm:3.370.0"],\ + ["@aws-sdk/middleware-recursion-detection", "npm:3.370.0"],\ + ["@aws-sdk/middleware-user-agent", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-endpoints", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-browser", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-node", "virtual:a9bd886ca27f871d002c65d71be20bce00ad3beae5ce61ef3b91b8088aeb133e0e165a852d092a26f89cb4a09b3ba4179aa731ed3340041afca9f58e099525b3#npm:3.370.0"],\ + ["@smithy/config-resolver", "npm:1.0.2"],\ + ["@smithy/fetch-http-handler", "npm:1.0.2"],\ + ["@smithy/hash-node", "npm:1.0.2"],\ + ["@smithy/invalid-dependency", "npm:1.0.2"],\ + ["@smithy/middleware-content-length", "npm:1.0.2"],\ + ["@smithy/middleware-endpoint", "npm:1.0.3"],\ + ["@smithy/middleware-retry", "npm:1.0.4"],\ + ["@smithy/middleware-serde", "npm:1.0.2"],\ + ["@smithy/middleware-stack", "npm:1.0.2"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/node-http-handler", "npm:1.0.3"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/smithy-client", "npm:1.0.4"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["@smithy/util-body-length-browser", "npm:1.0.2"],\ + ["@smithy/util-body-length-node", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-browser", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-node", "npm:1.0.2"],\ + ["@smithy/util-retry", "npm:1.0.4"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/client-sts", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-client-sts-npm-3.370.0-886ff7c162-55ce8a7a8a.zip/node_modules/@aws-sdk/client-sts/",\ + "packageDependencies": [\ + ["@aws-sdk/client-sts", "npm:3.370.0"],\ + ["@aws-crypto/sha256-browser", "npm:3.0.0"],\ + ["@aws-crypto/sha256-js", "npm:3.0.0"],\ + ["@aws-sdk/credential-provider-node", "npm:3.370.0"],\ + ["@aws-sdk/middleware-host-header", "npm:3.370.0"],\ + ["@aws-sdk/middleware-logger", "npm:3.370.0"],\ + ["@aws-sdk/middleware-recursion-detection", "npm:3.370.0"],\ + ["@aws-sdk/middleware-sdk-sts", "npm:3.370.0"],\ + ["@aws-sdk/middleware-signing", "npm:3.370.0"],\ + ["@aws-sdk/middleware-user-agent", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-endpoints", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-browser", "npm:3.370.0"],\ + ["@aws-sdk/util-user-agent-node", "virtual:a9bd886ca27f871d002c65d71be20bce00ad3beae5ce61ef3b91b8088aeb133e0e165a852d092a26f89cb4a09b3ba4179aa731ed3340041afca9f58e099525b3#npm:3.370.0"],\ + ["@smithy/config-resolver", "npm:1.0.2"],\ + ["@smithy/fetch-http-handler", "npm:1.0.2"],\ + ["@smithy/hash-node", "npm:1.0.2"],\ + ["@smithy/invalid-dependency", "npm:1.0.2"],\ + ["@smithy/middleware-content-length", "npm:1.0.2"],\ + ["@smithy/middleware-endpoint", "npm:1.0.3"],\ + ["@smithy/middleware-retry", "npm:1.0.4"],\ + ["@smithy/middleware-serde", "npm:1.0.2"],\ + ["@smithy/middleware-stack", "npm:1.0.2"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/node-http-handler", "npm:1.0.3"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/smithy-client", "npm:1.0.4"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["@smithy/util-body-length-browser", "npm:1.0.2"],\ + ["@smithy/util-body-length-node", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-browser", "npm:1.0.2"],\ + ["@smithy/util-defaults-mode-node", "npm:1.0.2"],\ + ["@smithy/util-retry", "npm:1.0.4"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["fast-xml-parser", "npm:4.2.5"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-cognito-identity", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-cognito-identity-npm-3.370.0-9f621fdb7e-3ee862b115.zip/node_modules/@aws-sdk/credential-provider-cognito-identity/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-cognito-identity", "npm:3.370.0"],\ + ["@aws-sdk/client-cognito-identity", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-env", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-env-npm-3.370.0-63ab7b9144-0295278ca3.zip/node_modules/@aws-sdk/credential-provider-env/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-env", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-ini", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.370.0-e1030b1253-8d8d0d386e.zip/node_modules/@aws-sdk/credential-provider-ini/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-ini", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-env", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-process", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-sso", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-web-identity", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/credential-provider-imds", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-node", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-node-npm-3.370.0-8675fb3d01-564422e77d.zip/node_modules/@aws-sdk/credential-provider-node/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-node", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-env", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-ini", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-process", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-sso", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-web-identity", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/credential-provider-imds", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-process", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-process-npm-3.370.0-5a4d7fd671-286080cfdb.zip/node_modules/@aws-sdk/credential-provider-process/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-process", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-sso", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.370.0-03241fdcc0-a5ef52fd21.zip/node_modules/@aws-sdk/credential-provider-sso/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-sso", "npm:3.370.0"],\ + ["@aws-sdk/client-sso", "npm:3.370.0"],\ + ["@aws-sdk/token-providers", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-provider-web-identity", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.370.0-da1f5214fa-3e9ce1c774.zip/node_modules/@aws-sdk/credential-provider-web-identity/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-provider-web-identity", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/credential-providers", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-credential-providers-npm-3.370.0-6591d3aac1-7eb60bda6b.zip/node_modules/@aws-sdk/credential-providers/",\ + "packageDependencies": [\ + ["@aws-sdk/credential-providers", "npm:3.370.0"],\ + ["@aws-sdk/client-cognito-identity", "npm:3.370.0"],\ + ["@aws-sdk/client-sso", "npm:3.370.0"],\ + ["@aws-sdk/client-sts", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-cognito-identity", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-env", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-ini", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-node", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-process", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-sso", "npm:3.370.0"],\ + ["@aws-sdk/credential-provider-web-identity", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/credential-provider-imds", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/middleware-host-header", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-middleware-host-header-npm-3.370.0-874d1a2ffd-2a7d3f3a0a.zip/node_modules/@aws-sdk/middleware-host-header/",\ + "packageDependencies": [\ + ["@aws-sdk/middleware-host-header", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/middleware-logger", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-middleware-logger-npm-3.370.0-12a9f2b942-f3c4062247.zip/node_modules/@aws-sdk/middleware-logger/",\ + "packageDependencies": [\ + ["@aws-sdk/middleware-logger", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/middleware-recursion-detection", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.370.0-914f181981-ee25951954.zip/node_modules/@aws-sdk/middleware-recursion-detection/",\ + "packageDependencies": [\ + ["@aws-sdk/middleware-recursion-detection", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/middleware-sdk-sts", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-middleware-sdk-sts-npm-3.370.0-ebc73af313-14877966d5.zip/node_modules/@aws-sdk/middleware-sdk-sts/",\ + "packageDependencies": [\ + ["@aws-sdk/middleware-sdk-sts", "npm:3.370.0"],\ + ["@aws-sdk/middleware-signing", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/middleware-signing", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-middleware-signing-npm-3.370.0-2cb6d89c62-f8581ad377.zip/node_modules/@aws-sdk/middleware-signing/",\ + "packageDependencies": [\ + ["@aws-sdk/middleware-signing", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/signature-v4", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-middleware", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/middleware-user-agent", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.370.0-179c9f8167-c4366db10a.zip/node_modules/@aws-sdk/middleware-user-agent/",\ + "packageDependencies": [\ + ["@aws-sdk/middleware-user-agent", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@aws-sdk/util-endpoints", "npm:3.370.0"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/token-providers", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-token-providers-npm-3.370.0-a1ea87e333-7126c5bdc8.zip/node_modules/@aws-sdk/token-providers/",\ + "packageDependencies": [\ + ["@aws-sdk/token-providers", "npm:3.370.0"],\ + ["@aws-sdk/client-sso-oidc", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/types", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-types-npm-3.370.0-25e9f06cca-105a5768f2.zip/node_modules/@aws-sdk/types/",\ + "packageDependencies": [\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/util-endpoints", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-util-endpoints-npm-3.370.0-a26911f59e-d351ad2fdc.zip/node_modules/@aws-sdk/util-endpoints/",\ + "packageDependencies": [\ + ["@aws-sdk/util-endpoints", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/util-locate-window", [\ + ["npm:3.310.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-util-locate-window-npm-3.310.0-0bb775a2bf-d552ce5f0f.zip/node_modules/@aws-sdk/util-locate-window/",\ + "packageDependencies": [\ + ["@aws-sdk/util-locate-window", "npm:3.310.0"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/util-user-agent-browser", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.370.0-15a59b38ca-3a549b1337.zip/node_modules/@aws-sdk/util-user-agent-browser/",\ + "packageDependencies": [\ + ["@aws-sdk/util-user-agent-browser", "npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["bowser", "npm:2.11.0"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/util-user-agent-node", [\ + ["npm:3.370.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.370.0-1bd47fb171-83b1f2c7f2.zip/node_modules/@aws-sdk/util-user-agent-node/",\ + "packageDependencies": [\ + ["@aws-sdk/util-user-agent-node", "npm:3.370.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:a9bd886ca27f871d002c65d71be20bce00ad3beae5ce61ef3b91b8088aeb133e0e165a852d092a26f89cb4a09b3ba4179aa731ed3340041afca9f58e099525b3#npm:3.370.0", {\ + "packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-bb43222e32/0/cache/@aws-sdk-util-user-agent-node-npm-3.370.0-1bd47fb171-83b1f2c7f2.zip/node_modules/@aws-sdk/util-user-agent-node/",\ + "packageDependencies": [\ + ["@aws-sdk/util-user-agent-node", "virtual:a9bd886ca27f871d002c65d71be20bce00ad3beae5ce61ef3b91b8088aeb133e0e165a852d092a26f89cb4a09b3ba4179aa731ed3340041afca9f58e099525b3#npm:3.370.0"],\ + ["@aws-sdk/types", "npm:3.370.0"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@types/aws-crt", null],\ + ["aws-crt", null],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "packagePeers": [\ + "@types/aws-crt",\ + "aws-crt"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@aws-sdk/util-utf8-browser", [\ + ["npm:3.259.0", {\ + "packageLocation": "./.yarn/cache/@aws-sdk-util-utf8-browser-npm-3.259.0-343a1dba08-b6a1e580da.zip/node_modules/@aws-sdk/util-utf8-browser/",\ + "packageDependencies": [\ + ["@aws-sdk/util-utf8-browser", "npm:3.259.0"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@babel/code-frame", [\ ["npm:7.22.5", {\ "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.22.5-b36f88d6f9-cfe804f518.zip/node_modules/@babel/code-frame/",\ @@ -1481,6 +2057,39 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@nestjs/axios", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@nestjs-axios-npm-3.0.0-a8fe50b1d1-0483ef5a10.zip/node_modules/@nestjs/axios/",\ + "packageDependencies": [\ + ["@nestjs/axios", "npm:3.0.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-axios-virtual-f25d9fbd3a/0/cache/@nestjs-axios-npm-3.0.0-a8fe50b1d1-0483ef5a10.zip/node_modules/@nestjs/axios/",\ + "packageDependencies": [\ + ["@nestjs/axios", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/axios", null],\ + ["@types/nestjs__common", null],\ + ["@types/reflect-metadata", null],\ + ["@types/rxjs", null],\ + ["axios", "npm:1.4.0"],\ + ["reflect-metadata", "npm:0.1.13"],\ + ["rxjs", "npm:7.8.1"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@types/axios",\ + "@types/nestjs__common",\ + "@types/reflect-metadata",\ + "@types/rxjs",\ + "reflect-metadata",\ + "rxjs"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@nestjs/cli", [\ ["npm:10.1.8", {\ "packageLocation": "./.yarn/cache/@nestjs-cli-npm-10.1.8-e16dfb8b46-6658580585.zip/node_modules/@nestjs/cli/",\ @@ -1545,8 +2154,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/class-validator", null],\ ["@types/reflect-metadata", null],\ ["@types/rxjs", null],\ - ["class-transformer", null],\ - ["class-validator", null],\ + ["class-transformer", "npm:0.5.1"],\ + ["class-validator", "npm:0.14.0"],\ ["iterare", "npm:1.2.1"],\ ["reflect-metadata", "npm:0.1.13"],\ ["rxjs", "npm:7.8.1"],\ @@ -1566,6 +2175,36 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@nestjs/config", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@nestjs-config-npm-3.0.0-e214dbc65d-e886dc337c.zip/node_modules/@nestjs/config/",\ + "packageDependencies": [\ + ["@nestjs/config", "npm:3.0.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-config-virtual-5984874a92/0/cache/@nestjs-config-npm-3.0.0-e214dbc65d-e886dc337c.zip/node_modules/@nestjs/config/",\ + "packageDependencies": [\ + ["@nestjs/config", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/nestjs__common", null],\ + ["@types/reflect-metadata", null],\ + ["dotenv", "npm:16.1.4"],\ + ["dotenv-expand", "npm:10.0.0"],\ + ["lodash", "npm:4.17.21"],\ + ["reflect-metadata", "npm:0.1.13"],\ + ["uuid", "npm:9.0.0"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@types/nestjs__common",\ + "@types/reflect-metadata",\ + "reflect-metadata"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@nestjs/core", [\ ["npm:10.0.5", {\ "packageLocation": "./.yarn/unplugged/@nestjs-core-virtual-0ff099f983/node_modules/@nestjs/core/",\ @@ -1614,39 +2253,160 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ - ["@nestjs/platform-express", [\ - ["npm:10.0.5", {\ - "packageLocation": "./.yarn/cache/@nestjs-platform-express-npm-10.0.5-bdde28fd9f-e7149c469a.zip/node_modules/@nestjs/platform-express/",\ + ["@nestjs/jwt", [\ + ["npm:10.1.0", {\ + "packageLocation": "./.yarn/cache/@nestjs-jwt-npm-10.1.0-3d70367215-c172b11ce1.zip/node_modules/@nestjs/jwt/",\ "packageDependencies": [\ - ["@nestjs/platform-express", "npm:10.0.5"]\ + ["@nestjs/jwt", "npm:10.1.0"]\ ],\ "linkType": "SOFT"\ }],\ - ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5", {\ - "packageLocation": "./.yarn/__virtual__/@nestjs-platform-express-virtual-dbbf8571ee/0/cache/@nestjs-platform-express-npm-10.0.5-bdde28fd9f-e7149c469a.zip/node_modules/@nestjs/platform-express/",\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-jwt-virtual-023805b8ab/0/cache/@nestjs-jwt-npm-10.1.0-3d70367215-c172b11ce1.zip/node_modules/@nestjs/jwt/",\ "packageDependencies": [\ - ["@nestjs/platform-express", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/jwt", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0"],\ ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ - ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/jsonwebtoken", "npm:9.0.2"],\ ["@types/nestjs__common", null],\ - ["@types/nestjs__core", null],\ - ["body-parser", "npm:1.20.2"],\ - ["cors", "npm:2.8.5"],\ - ["express", "npm:4.18.2"],\ - ["multer", "npm:1.4.4-lts.1"],\ - ["tslib", "npm:2.6.0"]\ + ["jsonwebtoken", "npm:9.0.0"]\ ],\ "packagePeers": [\ "@nestjs/common",\ - "@nestjs/core",\ - "@types/nestjs__common",\ - "@types/nestjs__core"\ + "@types/nestjs__common"\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@nestjs/schematics", [\ - ["npm:10.0.1", {\ + ["@nestjs/mapped-types", [\ + ["npm:2.0.2", {\ + "packageLocation": "./.yarn/cache/@nestjs-mapped-types-npm-2.0.2-3b001953e2-1fb0dca383.zip/node_modules/@nestjs/mapped-types/",\ + "packageDependencies": [\ + ["@nestjs/mapped-types", "npm:2.0.2"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:2.0.2", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-mapped-types-virtual-d60671e166/0/cache/@nestjs-mapped-types-npm-2.0.2-3b001953e2-1fb0dca383.zip/node_modules/@nestjs/mapped-types/",\ + "packageDependencies": [\ + ["@nestjs/mapped-types", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:2.0.2"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/class-transformer", null],\ + ["@types/class-validator", null],\ + ["@types/nestjs__common", null],\ + ["@types/reflect-metadata", null],\ + ["class-transformer", "npm:0.5.1"],\ + ["class-validator", "npm:0.14.0"],\ + ["reflect-metadata", "npm:0.1.13"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@types/class-transformer",\ + "@types/class-validator",\ + "@types/nestjs__common",\ + "@types/reflect-metadata",\ + "class-transformer",\ + "class-validator",\ + "reflect-metadata"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nestjs/mongoose", [\ + ["npm:10.0.0", {\ + "packageLocation": "./.yarn/cache/@nestjs-mongoose-npm-10.0.0-0bac3c9c0e-faf75fdc24.zip/node_modules/@nestjs/mongoose/",\ + "packageDependencies": [\ + ["@nestjs/mongoose", "npm:10.0.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-mongoose-virtual-a240a8ea51/0/cache/@nestjs-mongoose-npm-10.0.0-0bac3c9c0e-faf75fdc24.zip/node_modules/@nestjs/mongoose/",\ + "packageDependencies": [\ + ["@nestjs/mongoose", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/mongoose", null],\ + ["@types/nestjs__common", null],\ + ["@types/nestjs__core", null],\ + ["@types/reflect-metadata", null],\ + ["@types/rxjs", null],\ + ["mongoose", "npm:7.3.4"],\ + ["reflect-metadata", "npm:0.1.13"],\ + ["rxjs", "npm:7.8.1"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@nestjs/core",\ + "@types/mongoose",\ + "@types/nestjs__common",\ + "@types/nestjs__core",\ + "@types/reflect-metadata",\ + "@types/rxjs",\ + "mongoose",\ + "reflect-metadata",\ + "rxjs"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nestjs/passport", [\ + ["npm:10.0.0", {\ + "packageLocation": "./.yarn/cache/@nestjs-passport-npm-10.0.0-c1f99da0b6-5443695832.zip/node_modules/@nestjs/passport/",\ + "packageDependencies": [\ + ["@nestjs/passport", "npm:10.0.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-passport-virtual-fdc0f7110e/0/cache/@nestjs-passport-npm-10.0.0-c1f99da0b6-5443695832.zip/node_modules/@nestjs/passport/",\ + "packageDependencies": [\ + ["@nestjs/passport", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/nestjs__common", null],\ + ["@types/passport", null],\ + ["passport", "npm:0.6.0"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@types/nestjs__common",\ + "@types/passport"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nestjs/platform-express", [\ + ["npm:10.0.5", {\ + "packageLocation": "./.yarn/cache/@nestjs-platform-express-npm-10.0.5-bdde28fd9f-e7149c469a.zip/node_modules/@nestjs/platform-express/",\ + "packageDependencies": [\ + ["@nestjs/platform-express", "npm:10.0.5"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-platform-express-virtual-dbbf8571ee/0/cache/@nestjs-platform-express-npm-10.0.5-bdde28fd9f-e7149c469a.zip/node_modules/@nestjs/platform-express/",\ + "packageDependencies": [\ + ["@nestjs/platform-express", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/nestjs__common", null],\ + ["@types/nestjs__core", null],\ + ["body-parser", "npm:1.20.2"],\ + ["cors", "npm:2.8.5"],\ + ["express", "npm:4.18.2"],\ + ["multer", "npm:1.4.4-lts.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@nestjs/core",\ + "@types/nestjs__common",\ + "@types/nestjs__core"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nestjs/schematics", [\ + ["npm:10.0.1", {\ "packageLocation": "./.yarn/cache/@nestjs-schematics-npm-10.0.1-6d2b5a2747-111fa24df8.zip/node_modules/@nestjs/schematics/",\ "packageDependencies": [\ ["@nestjs/schematics", "npm:10.0.1"]\ @@ -1672,143 +2432,598 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ - ["@nestjs/testing", [\ - ["npm:10.0.5", {\ - "packageLocation": "./.yarn/cache/@nestjs-testing-npm-10.0.5-7ab4fac79c-dfc6b492ca.zip/node_modules/@nestjs/testing/",\ - "packageDependencies": [\ - ["@nestjs/testing", "npm:10.0.5"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5", {\ - "packageLocation": "./.yarn/__virtual__/@nestjs-testing-virtual-54d371263e/0/cache/@nestjs-testing-npm-10.0.5-7ab4fac79c-dfc6b492ca.zip/node_modules/@nestjs/testing/",\ + ["@nestjs/testing", [\ + ["npm:10.1.0", {\ + "packageLocation": "./.yarn/cache/@nestjs-testing-npm-10.1.0-fefe10019d-80e609e4d0.zip/node_modules/@nestjs/testing/",\ + "packageDependencies": [\ + ["@nestjs/testing", "npm:10.1.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0", {\ + "packageLocation": "./.yarn/__virtual__/@nestjs-testing-virtual-2d56f71a9e/0/cache/@nestjs-testing-npm-10.1.0-fefe10019d-80e609e4d0.zip/node_modules/@nestjs/testing/",\ + "packageDependencies": [\ + ["@nestjs/testing", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0"],\ + ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/microservices", null],\ + ["@nestjs/platform-express", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@types/nestjs__common", null],\ + ["@types/nestjs__core", null],\ + ["@types/nestjs__microservices", null],\ + ["@types/nestjs__platform-express", null],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "packagePeers": [\ + "@nestjs/common",\ + "@nestjs/core",\ + "@nestjs/microservices",\ + "@nestjs/platform-express",\ + "@types/nestjs__common",\ + "@types/nestjs__core",\ + "@types/nestjs__microservices",\ + "@types/nestjs__platform-express"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nodelib/fs.scandir", [\ + ["npm:2.1.5", {\ + "packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip/node_modules/@nodelib/fs.scandir/",\ + "packageDependencies": [\ + ["@nodelib/fs.scandir", "npm:2.1.5"],\ + ["@nodelib/fs.stat", "npm:2.0.5"],\ + ["run-parallel", "npm:1.2.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nodelib/fs.stat", [\ + ["npm:2.0.5", {\ + "packageLocation": "./.yarn/cache/@nodelib-fs.stat-npm-2.0.5-01f4dd3030-012480b5ca.zip/node_modules/@nodelib/fs.stat/",\ + "packageDependencies": [\ + ["@nodelib/fs.stat", "npm:2.0.5"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nodelib/fs.walk", [\ + ["npm:1.2.8", {\ + "packageLocation": "./.yarn/cache/@nodelib-fs.walk-npm-1.2.8-b4a89da548-190c643f15.zip/node_modules/@nodelib/fs.walk/",\ + "packageDependencies": [\ + ["@nodelib/fs.walk", "npm:1.2.8"],\ + ["@nodelib/fs.scandir", "npm:2.1.5"],\ + ["fastq", "npm:1.15.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@npmcli/fs", [\ + ["npm:3.1.0", {\ + "packageLocation": "./.yarn/cache/@npmcli-fs-npm-3.1.0-0844a57978-a50a6818de.zip/node_modules/@npmcli/fs/",\ + "packageDependencies": [\ + ["@npmcli/fs", "npm:3.1.0"],\ + ["semver", "npm:7.5.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@nuxtjs/opencollective", [\ + ["npm:0.3.2", {\ + "packageLocation": "./.yarn/cache/@nuxtjs-opencollective-npm-0.3.2-72db6b3551-fd3737c12e.zip/node_modules/@nuxtjs/opencollective/",\ + "packageDependencies": [\ + ["@nuxtjs/opencollective", "npm:0.3.2"],\ + ["chalk", "npm:4.1.2"],\ + ["consola", "npm:2.15.3"],\ + ["node-fetch", "virtual:72db6b3551c1b46986e711aee96d1643b26ff6991672401c35442adfbef36d10ccd9289b58e518aac04afe4e688ca4b130da9fae9c1c040113a2585b8cfeea9a#npm:2.6.12"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@pkgjs/parseargs", [\ + ["npm:0.11.0", {\ + "packageLocation": "./.yarn/cache/@pkgjs-parseargs-npm-0.11.0-cd2a3fe948-6ad6a00fc4.zip/node_modules/@pkgjs/parseargs/",\ + "packageDependencies": [\ + ["@pkgjs/parseargs", "npm:0.11.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@pkgr/utils", [\ + ["npm:2.4.2", {\ + "packageLocation": "./.yarn/cache/@pkgr-utils-npm-2.4.2-5333ff17f3-24e04c1212.zip/node_modules/@pkgr/utils/",\ + "packageDependencies": [\ + ["@pkgr/utils", "npm:2.4.2"],\ + ["cross-spawn", "npm:7.0.3"],\ + ["fast-glob", "npm:3.3.0"],\ + ["is-glob", "npm:4.0.3"],\ + ["open", "npm:9.1.0"],\ + ["picocolors", "npm:1.0.0"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@sinclair/typebox", [\ + ["npm:0.27.8", {\ + "packageLocation": "./.yarn/cache/@sinclair-typebox-npm-0.27.8-23e206d653-00bd7362a3.zip/node_modules/@sinclair/typebox/",\ + "packageDependencies": [\ + ["@sinclair/typebox", "npm:0.27.8"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@sinonjs/commons", [\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/@sinonjs-commons-npm-3.0.0-fa72ff71a1-b4b5b73d4d.zip/node_modules/@sinonjs/commons/",\ + "packageDependencies": [\ + ["@sinonjs/commons", "npm:3.0.0"],\ + ["type-detect", "npm:4.0.8"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@sinonjs/fake-timers", [\ + ["npm:10.3.0", {\ + "packageLocation": "./.yarn/cache/@sinonjs-fake-timers-npm-10.3.0-7417f876b4-614d30cb4d.zip/node_modules/@sinonjs/fake-timers/",\ + "packageDependencies": [\ + ["@sinonjs/fake-timers", "npm:10.3.0"],\ + ["@sinonjs/commons", "npm:3.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/abort-controller", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-abort-controller-npm-1.0.2-7f32adc09f-5d0c5b0463.zip/node_modules/@smithy/abort-controller/",\ + "packageDependencies": [\ + ["@smithy/abort-controller", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/config-resolver", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-config-resolver-npm-1.0.2-0f50d5f700-23efcb59d6.zip/node_modules/@smithy/config-resolver/",\ + "packageDependencies": [\ + ["@smithy/config-resolver", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-config-provider", "npm:1.0.2"],\ + ["@smithy/util-middleware", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/credential-provider-imds", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-credential-provider-imds-npm-1.0.2-08dc132d2c-91b57891b6.zip/node_modules/@smithy/credential-provider-imds/",\ + "packageDependencies": [\ + ["@smithy/credential-provider-imds", "npm:1.0.2"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/eventstream-codec", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-eventstream-codec-npm-1.0.2-c7c5feddbc-64e2f6e5b0.zip/node_modules/@smithy/eventstream-codec/",\ + "packageDependencies": [\ + ["@smithy/eventstream-codec", "npm:1.0.2"],\ + ["@aws-crypto/crc32", "npm:3.0.0"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-hex-encoding", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/fetch-http-handler", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-fetch-http-handler-npm-1.0.2-ad97dc7181-62965f69eb.zip/node_modules/@smithy/fetch-http-handler/",\ + "packageDependencies": [\ + ["@smithy/fetch-http-handler", "npm:1.0.2"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/querystring-builder", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/hash-node", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-hash-node-npm-1.0.2-efde6bd2e2-018ef3271c.zip/node_modules/@smithy/hash-node/",\ + "packageDependencies": [\ + ["@smithy/hash-node", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-buffer-from", "npm:1.0.2"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/invalid-dependency", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-invalid-dependency-npm-1.0.2-c3c4b73575-51d4bfc258.zip/node_modules/@smithy/invalid-dependency/",\ + "packageDependencies": [\ + ["@smithy/invalid-dependency", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/is-array-buffer", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-is-array-buffer-npm-1.0.2-a8ef5bb507-811b100b80.zip/node_modules/@smithy/is-array-buffer/",\ + "packageDependencies": [\ + ["@smithy/is-array-buffer", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/middleware-content-length", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-middleware-content-length-npm-1.0.2-b108132555-4b00aa7411.zip/node_modules/@smithy/middleware-content-length/",\ + "packageDependencies": [\ + ["@smithy/middleware-content-length", "npm:1.0.2"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/middleware-endpoint", [\ + ["npm:1.0.3", {\ + "packageLocation": "./.yarn/cache/@smithy-middleware-endpoint-npm-1.0.3-81e4f50747-8081b51595.zip/node_modules/@smithy/middleware-endpoint/",\ + "packageDependencies": [\ + ["@smithy/middleware-endpoint", "npm:1.0.3"],\ + ["@smithy/middleware-serde", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["@smithy/util-middleware", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/middleware-retry", [\ + ["npm:1.0.4", {\ + "packageLocation": "./.yarn/cache/@smithy-middleware-retry-npm-1.0.4-db58446b68-3ca5c4abe3.zip/node_modules/@smithy/middleware-retry/",\ + "packageDependencies": [\ + ["@smithy/middleware-retry", "npm:1.0.4"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/service-error-classification", "npm:1.0.3"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-middleware", "npm:1.0.2"],\ + ["@smithy/util-retry", "npm:1.0.4"],\ + ["tslib", "npm:2.6.0"],\ + ["uuid", "npm:8.3.2"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/middleware-serde", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-middleware-serde-npm-1.0.2-9a3fabbdce-563045c0ad.zip/node_modules/@smithy/middleware-serde/",\ + "packageDependencies": [\ + ["@smithy/middleware-serde", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/middleware-stack", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-middleware-stack-npm-1.0.2-569268495f-34794d1e6d.zip/node_modules/@smithy/middleware-stack/",\ + "packageDependencies": [\ + ["@smithy/middleware-stack", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/node-config-provider", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-node-config-provider-npm-1.0.2-b1838ef775-593bd5adf0.zip/node_modules/@smithy/node-config-provider/",\ + "packageDependencies": [\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/node-http-handler", [\ + ["npm:1.0.3", {\ + "packageLocation": "./.yarn/cache/@smithy-node-http-handler-npm-1.0.3-e77f20770f-727e1391cc.zip/node_modules/@smithy/node-http-handler/",\ + "packageDependencies": [\ + ["@smithy/node-http-handler", "npm:1.0.3"],\ + ["@smithy/abort-controller", "npm:1.0.2"],\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/querystring-builder", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/property-provider", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-property-provider-npm-1.0.2-0958386554-55d09de3da.zip/node_modules/@smithy/property-provider/",\ + "packageDependencies": [\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/protocol-http", [\ + ["npm:1.1.1", {\ + "packageLocation": "./.yarn/cache/@smithy-protocol-http-npm-1.1.1-a6955fb975-6320e8b010.zip/node_modules/@smithy/protocol-http/",\ + "packageDependencies": [\ + ["@smithy/protocol-http", "npm:1.1.1"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/querystring-builder", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-querystring-builder-npm-1.0.2-cd386247de-c0e6807a5b.zip/node_modules/@smithy/querystring-builder/",\ + "packageDependencies": [\ + ["@smithy/querystring-builder", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-uri-escape", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/querystring-parser", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-querystring-parser-npm-1.0.2-588300d020-348672b1bc.zip/node_modules/@smithy/querystring-parser/",\ + "packageDependencies": [\ + ["@smithy/querystring-parser", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/service-error-classification", [\ + ["npm:1.0.3", {\ + "packageLocation": "./.yarn/cache/@smithy-service-error-classification-npm-1.0.3-626eea7f1d-adf840de78.zip/node_modules/@smithy/service-error-classification/",\ + "packageDependencies": [\ + ["@smithy/service-error-classification", "npm:1.0.3"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/shared-ini-file-loader", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-shared-ini-file-loader-npm-1.0.2-fe69af7fa4-c9ba908a48.zip/node_modules/@smithy/shared-ini-file-loader/",\ + "packageDependencies": [\ + ["@smithy/shared-ini-file-loader", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/signature-v4", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-signature-v4-npm-1.0.2-46dc22d0e0-3e3072ef95.zip/node_modules/@smithy/signature-v4/",\ + "packageDependencies": [\ + ["@smithy/signature-v4", "npm:1.0.2"],\ + ["@smithy/eventstream-codec", "npm:1.0.2"],\ + ["@smithy/is-array-buffer", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-hex-encoding", "npm:1.0.2"],\ + ["@smithy/util-middleware", "npm:1.0.2"],\ + ["@smithy/util-uri-escape", "npm:1.0.2"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/smithy-client", [\ + ["npm:1.0.4", {\ + "packageLocation": "./.yarn/cache/@smithy-smithy-client-npm-1.0.4-38923c234a-468aa2f28b.zip/node_modules/@smithy/smithy-client/",\ + "packageDependencies": [\ + ["@smithy/smithy-client", "npm:1.0.4"],\ + ["@smithy/middleware-stack", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-stream", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/types", [\ + ["npm:1.1.1", {\ + "packageLocation": "./.yarn/cache/@smithy-types-npm-1.1.1-77d4550f69-bf4b632eb7.zip/node_modules/@smithy/types/",\ + "packageDependencies": [\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/url-parser", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-url-parser-npm-1.0.2-834c0e5097-1844b23a30.zip/node_modules/@smithy/url-parser/",\ + "packageDependencies": [\ + ["@smithy/url-parser", "npm:1.0.2"],\ + ["@smithy/querystring-parser", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/util-base64", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-base64-npm-1.0.2-233054ecba-618d2952fb.zip/node_modules/@smithy/util-base64/",\ + "packageDependencies": [\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["@smithy/util-buffer-from", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/util-body-length-browser", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-body-length-browser-npm-1.0.2-dbde19a47e-3b0eacc376.zip/node_modules/@smithy/util-body-length-browser/",\ + "packageDependencies": [\ + ["@smithy/util-body-length-browser", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@smithy/util-body-length-node", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-body-length-node-npm-1.0.2-8f10cf41cb-472371dc0a.zip/node_modules/@smithy/util-body-length-node/",\ "packageDependencies": [\ - ["@nestjs/testing", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ - ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ - ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ - ["@nestjs/microservices", null],\ - ["@nestjs/platform-express", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ - ["@types/nestjs__common", null],\ - ["@types/nestjs__core", null],\ - ["@types/nestjs__microservices", null],\ - ["@types/nestjs__platform-express", null],\ + ["@smithy/util-body-length-node", "npm:1.0.2"],\ ["tslib", "npm:2.6.0"]\ ],\ - "packagePeers": [\ - "@nestjs/common",\ - "@nestjs/core",\ - "@nestjs/microservices",\ - "@nestjs/platform-express",\ - "@types/nestjs__common",\ - "@types/nestjs__core",\ - "@types/nestjs__microservices",\ - "@types/nestjs__platform-express"\ - ],\ "linkType": "HARD"\ }]\ ]],\ - ["@nodelib/fs.scandir", [\ - ["npm:2.1.5", {\ - "packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip/node_modules/@nodelib/fs.scandir/",\ + ["@smithy/util-buffer-from", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-buffer-from-npm-1.0.2-58dd9f842b-ca4308ebc5.zip/node_modules/@smithy/util-buffer-from/",\ "packageDependencies": [\ - ["@nodelib/fs.scandir", "npm:2.1.5"],\ - ["@nodelib/fs.stat", "npm:2.0.5"],\ - ["run-parallel", "npm:1.2.0"]\ + ["@smithy/util-buffer-from", "npm:1.0.2"],\ + ["@smithy/is-array-buffer", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@nodelib/fs.stat", [\ - ["npm:2.0.5", {\ - "packageLocation": "./.yarn/cache/@nodelib-fs.stat-npm-2.0.5-01f4dd3030-012480b5ca.zip/node_modules/@nodelib/fs.stat/",\ + ["@smithy/util-config-provider", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-config-provider-npm-1.0.2-d61fd81ad6-625c100b8a.zip/node_modules/@smithy/util-config-provider/",\ "packageDependencies": [\ - ["@nodelib/fs.stat", "npm:2.0.5"]\ + ["@smithy/util-config-provider", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@nodelib/fs.walk", [\ - ["npm:1.2.8", {\ - "packageLocation": "./.yarn/cache/@nodelib-fs.walk-npm-1.2.8-b4a89da548-190c643f15.zip/node_modules/@nodelib/fs.walk/",\ + ["@smithy/util-defaults-mode-browser", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-defaults-mode-browser-npm-1.0.2-4be5a50c54-ca03c9f596.zip/node_modules/@smithy/util-defaults-mode-browser/",\ "packageDependencies": [\ - ["@nodelib/fs.walk", "npm:1.2.8"],\ - ["@nodelib/fs.scandir", "npm:2.1.5"],\ - ["fastq", "npm:1.15.0"]\ + ["@smithy/util-defaults-mode-browser", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["bowser", "npm:2.11.0"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@npmcli/fs", [\ - ["npm:3.1.0", {\ - "packageLocation": "./.yarn/cache/@npmcli-fs-npm-3.1.0-0844a57978-a50a6818de.zip/node_modules/@npmcli/fs/",\ - "packageDependencies": [\ - ["@npmcli/fs", "npm:3.1.0"],\ - ["semver", "npm:7.5.4"]\ + ["@smithy/util-defaults-mode-node", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-defaults-mode-node-npm-1.0.2-e9f11134c0-805977758a.zip/node_modules/@smithy/util-defaults-mode-node/",\ + "packageDependencies": [\ + ["@smithy/util-defaults-mode-node", "npm:1.0.2"],\ + ["@smithy/config-resolver", "npm:1.0.2"],\ + ["@smithy/credential-provider-imds", "npm:1.0.2"],\ + ["@smithy/node-config-provider", "npm:1.0.2"],\ + ["@smithy/property-provider", "npm:1.0.2"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@nuxtjs/opencollective", [\ - ["npm:0.3.2", {\ - "packageLocation": "./.yarn/cache/@nuxtjs-opencollective-npm-0.3.2-72db6b3551-fd3737c12e.zip/node_modules/@nuxtjs/opencollective/",\ + ["@smithy/util-hex-encoding", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-hex-encoding-npm-1.0.2-701dc5e90a-c70bfccb41.zip/node_modules/@smithy/util-hex-encoding/",\ "packageDependencies": [\ - ["@nuxtjs/opencollective", "npm:0.3.2"],\ - ["chalk", "npm:4.1.2"],\ - ["consola", "npm:2.15.3"],\ - ["node-fetch", "virtual:72db6b3551c1b46986e711aee96d1643b26ff6991672401c35442adfbef36d10ccd9289b58e518aac04afe4e688ca4b130da9fae9c1c040113a2585b8cfeea9a#npm:2.6.12"]\ + ["@smithy/util-hex-encoding", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@pkgjs/parseargs", [\ - ["npm:0.11.0", {\ - "packageLocation": "./.yarn/cache/@pkgjs-parseargs-npm-0.11.0-cd2a3fe948-6ad6a00fc4.zip/node_modules/@pkgjs/parseargs/",\ + ["@smithy/util-middleware", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-middleware-npm-1.0.2-92048f4c53-899aca62ef.zip/node_modules/@smithy/util-middleware/",\ "packageDependencies": [\ - ["@pkgjs/parseargs", "npm:0.11.0"]\ + ["@smithy/util-middleware", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@pkgr/utils", [\ - ["npm:2.4.2", {\ - "packageLocation": "./.yarn/cache/@pkgr-utils-npm-2.4.2-5333ff17f3-24e04c1212.zip/node_modules/@pkgr/utils/",\ + ["@smithy/util-retry", [\ + ["npm:1.0.4", {\ + "packageLocation": "./.yarn/cache/@smithy-util-retry-npm-1.0.4-3e2ede7318-b9a7e464d6.zip/node_modules/@smithy/util-retry/",\ "packageDependencies": [\ - ["@pkgr/utils", "npm:2.4.2"],\ - ["cross-spawn", "npm:7.0.3"],\ - ["fast-glob", "npm:3.3.0"],\ - ["is-glob", "npm:4.0.3"],\ - ["open", "npm:9.1.0"],\ - ["picocolors", "npm:1.0.0"],\ + ["@smithy/util-retry", "npm:1.0.4"],\ + ["@smithy/service-error-classification", "npm:1.0.3"],\ ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@sinclair/typebox", [\ - ["npm:0.27.8", {\ - "packageLocation": "./.yarn/cache/@sinclair-typebox-npm-0.27.8-23e206d653-00bd7362a3.zip/node_modules/@sinclair/typebox/",\ - "packageDependencies": [\ - ["@sinclair/typebox", "npm:0.27.8"]\ + ["@smithy/util-stream", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-stream-npm-1.0.2-4ccf01d6d2-ebea688676.zip/node_modules/@smithy/util-stream/",\ + "packageDependencies": [\ + ["@smithy/util-stream", "npm:1.0.2"],\ + ["@smithy/fetch-http-handler", "npm:1.0.2"],\ + ["@smithy/node-http-handler", "npm:1.0.3"],\ + ["@smithy/types", "npm:1.1.1"],\ + ["@smithy/util-base64", "npm:1.0.2"],\ + ["@smithy/util-buffer-from", "npm:1.0.2"],\ + ["@smithy/util-hex-encoding", "npm:1.0.2"],\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@sinonjs/commons", [\ - ["npm:3.0.0", {\ - "packageLocation": "./.yarn/cache/@sinonjs-commons-npm-3.0.0-fa72ff71a1-b4b5b73d4d.zip/node_modules/@sinonjs/commons/",\ + ["@smithy/util-uri-escape", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-uri-escape-npm-1.0.2-7a2e0f5e15-6df9fb3aed.zip/node_modules/@smithy/util-uri-escape/",\ "packageDependencies": [\ - ["@sinonjs/commons", "npm:3.0.0"],\ - ["type-detect", "npm:4.0.8"]\ + ["@smithy/util-uri-escape", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\ - ["@sinonjs/fake-timers", [\ - ["npm:10.3.0", {\ - "packageLocation": "./.yarn/cache/@sinonjs-fake-timers-npm-10.3.0-7417f876b4-614d30cb4d.zip/node_modules/@sinonjs/fake-timers/",\ + ["@smithy/util-utf8", [\ + ["npm:1.0.2", {\ + "packageLocation": "./.yarn/cache/@smithy-util-utf8-npm-1.0.2-64fd50c87a-64add10ac1.zip/node_modules/@smithy/util-utf8/",\ "packageDependencies": [\ - ["@sinonjs/fake-timers", "npm:10.3.0"],\ - ["@sinonjs/commons", "npm:3.0.0"]\ + ["@smithy/util-utf8", "npm:1.0.2"],\ + ["@smithy/util-buffer-from", "npm:1.0.2"],\ + ["tslib", "npm:2.6.0"]\ ],\ "linkType": "HARD"\ }]\ @@ -1924,6 +3139,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@types/cookie-parser", [\ + ["npm:1.4.3", {\ + "packageLocation": "./.yarn/cache/@types-cookie-parser-npm-1.4.3-4bda65954a-f390f3af1b.zip/node_modules/@types/cookie-parser/",\ + "packageDependencies": [\ + ["@types/cookie-parser", "npm:1.4.3"],\ + ["@types/express", "npm:4.17.17"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@types/cookiejar", [\ ["npm:2.1.2", {\ "packageLocation": "./.yarn/cache/@types-cookiejar-npm-2.1.2-2588120a7c-f6e1903454.zip/node_modules/@types/cookiejar/",\ @@ -2067,6 +3292,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@types/jsonwebtoken", [\ + ["npm:9.0.2", {\ + "packageLocation": "./.yarn/cache/@types-jsonwebtoken-npm-9.0.2-7af15f7b9b-3bb8d40e78.zip/node_modules/@types/jsonwebtoken/",\ + "packageDependencies": [\ + ["@types/jsonwebtoken", "npm:9.0.2"],\ + ["@types/node", "npm:20.4.2"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@types/mime", [\ ["npm:1.3.2", {\ "packageLocation": "./.yarn/cache/@types-mime-npm-1.3.2-ea71878ab3-0493368244.zip/node_modules/@types/mime/",\ @@ -2083,6 +3318,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@types/multer", [\ + ["npm:1.4.7", {\ + "packageLocation": "./.yarn/cache/@types-multer-npm-1.4.7-df3819be8e-680cb0710a.zip/node_modules/@types/multer/",\ + "packageDependencies": [\ + ["@types/multer", "npm:1.4.7"],\ + ["@types/express", "npm:4.17.17"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@types/node", [\ ["npm:20.4.2", {\ "packageLocation": "./.yarn/cache/@types-node-npm-20.4.2-0b33863e43-99e544ea75.zip/node_modules/@types/node/",\ @@ -2190,6 +3435,35 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@types/validator", [\ + ["npm:13.7.17", {\ + "packageLocation": "./.yarn/cache/@types-validator-npm-13.7.17-200baed5c4-a827e480c0.zip/node_modules/@types/validator/",\ + "packageDependencies": [\ + ["@types/validator", "npm:13.7.17"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@types/webidl-conversions", [\ + ["npm:7.0.0", {\ + "packageLocation": "./.yarn/cache/@types-webidl-conversions-npm-7.0.0-0903313151-60142c7ddd.zip/node_modules/@types/webidl-conversions/",\ + "packageDependencies": [\ + ["@types/webidl-conversions", "npm:7.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@types/whatwg-url", [\ + ["npm:8.2.2", {\ + "packageLocation": "./.yarn/cache/@types-whatwg-url-npm-8.2.2-54c5c24e6c-5dc5afe078.zip/node_modules/@types/whatwg-url/",\ + "packageDependencies": [\ + ["@types/whatwg-url", "npm:8.2.2"],\ + ["@types/node", "npm:20.4.2"],\ + ["@types/webidl-conversions", "npm:7.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@types/yargs", [\ ["npm:17.0.24", {\ "packageLocation": "./.yarn/cache/@types-yargs-npm-17.0.24-b034cf1d8b-5f3ac4dc4f.zip/node_modules/@types/yargs/",\ @@ -2613,24 +3887,42 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./",\ "packageDependencies": [\ ["SEOKO-server", "workspace:."],\ + ["@nestjs/axios", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0"],\ ["@nestjs/cli", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.8"],\ ["@nestjs/common", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/config", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.0.0"],\ ["@nestjs/core", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/jwt", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0"],\ + ["@nestjs/mapped-types", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:2.0.2"],\ + ["@nestjs/mongoose", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0"],\ + ["@nestjs/passport", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.0"],\ ["@nestjs/platform-express", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ ["@nestjs/schematics", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.1"],\ - ["@nestjs/testing", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.0.5"],\ + ["@nestjs/testing", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:10.1.0"],\ + ["@types/cookie-parser", "npm:1.4.3"],\ ["@types/express", "npm:4.17.17"],\ ["@types/jest", "npm:29.5.3"],\ + ["@types/multer", "npm:1.4.7"],\ ["@types/node", "npm:20.4.2"],\ ["@types/supertest", "npm:2.0.12"],\ ["@typescript-eslint/eslint-plugin", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:5.62.0"],\ ["@typescript-eslint/parser", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:5.62.0"],\ + ["bcryptjs", "npm:2.4.3"],\ + ["class-transformer", "npm:0.5.1"],\ + ["class-validator", "npm:0.14.0"],\ + ["cookie-parser", "npm:1.4.6"],\ + ["crypto-js", "npm:4.1.1"],\ ["eslint", "npm:8.45.0"],\ ["eslint-config-prettier", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:8.8.0"],\ ["eslint-import-resolver-typescript", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:3.5.5"],\ ["eslint-plugin-import", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:2.27.5"],\ ["eslint-plugin-prettier", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:4.2.1"],\ ["jest", "virtual:4ab331f8e2373e50cdaa1443c30fdf7585ecd05ba1a7a36ff340c67bd16e17b9f5b88718db9a94325bc4a8111d50636a6cef8fdaa6276a7b88970a8810002d0e#npm:29.6.1"],\ + ["mongodb-memory-server", "npm:8.13.0"],\ + ["mongoose", "npm:7.3.4"],\ + ["multer", "npm:1.4.5-lts.1"],\ + ["passport-jwt", "npm:4.0.1"],\ + ["passport-local", "npm:1.0.0"],\ ["prettier", "npm:2.8.8"],\ ["reflect-metadata", "npm:0.1.13"],\ ["rxjs", "npm:7.8.1"],\ @@ -3048,6 +4340,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["async-mutex", [\ + ["npm:0.3.2", {\ + "packageLocation": "./.yarn/cache/async-mutex-npm-0.3.2-600f6c46a1-620b771dfd.zip/node_modules/async-mutex/",\ + "packageDependencies": [\ + ["async-mutex", "npm:0.3.2"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["asynckit", [\ ["npm:0.4.0", {\ "packageLocation": "./.yarn/cache/asynckit-npm-0.4.0-c718858525-7b78c451df.zip/node_modules/asynckit/",\ @@ -3066,6 +4368,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["axios", [\ + ["npm:1.4.0", {\ + "packageLocation": "./.yarn/cache/axios-npm-1.4.0-4d7ce8ca3e-7fb6a4313b.zip/node_modules/axios/",\ + "packageDependencies": [\ + ["axios", "npm:1.4.0"],\ + ["follow-redirects", "virtual:4d7ce8ca3e1e44d82523fba2ad95e1be18c4e9f8dec6d551377587540da3ed75bd8bd3e812280309a3b90cfdb0560f076f3552a20839f7f15665207a4fbd588a#npm:1.15.2"],\ + ["form-data", "npm:4.0.0"],\ + ["proxy-from-env", "npm:1.1.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["babel-jest", [\ ["npm:29.6.1", {\ "packageLocation": "./.yarn/cache/babel-jest-npm-29.6.1-d3591ae5ed-bc46cfba46.zip/node_modules/babel-jest/",\ @@ -3221,6 +4535,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["bcryptjs", [\ + ["npm:2.4.3", {\ + "packageLocation": "./.yarn/cache/bcryptjs-npm-2.4.3-32de4957eb-0e80ed852a.zip/node_modules/bcryptjs/",\ + "packageDependencies": [\ + ["bcryptjs", "npm:2.4.3"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["big-integer", [\ ["npm:1.6.51", {\ "packageLocation": "./.yarn/cache/big-integer-npm-1.6.51-1a244d8e1f-3d444173d1.zip/node_modules/big-integer/",\ @@ -3291,6 +4614,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["bowser", [\ + ["npm:2.11.0", {\ + "packageLocation": "./.yarn/cache/bowser-npm-2.11.0-33664d9063-29c3f01f22.zip/node_modules/bowser/",\ + "packageDependencies": [\ + ["bowser", "npm:2.11.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["bplist-parser", [\ ["npm:0.2.0", {\ "packageLocation": "./.yarn/cache/bplist-parser-npm-0.2.0-91a681e495-d5339dd16a.zip/node_modules/bplist-parser/",\ @@ -3363,6 +4695,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["bson", [\ + ["npm:4.7.2", {\ + "packageLocation": "./.yarn/cache/bson-npm-4.7.2-77a08a4d01-f357d12c56.zip/node_modules/bson/",\ + "packageDependencies": [\ + ["bson", "npm:4.7.2"],\ + ["buffer", "npm:5.7.1"]\ + ],\ + "linkType": "HARD"\ + }],\ + ["npm:5.4.0", {\ + "packageLocation": "./.yarn/cache/bson-npm-5.4.0-2f854c8216-1c07e3d09f.zip/node_modules/bson/",\ + "packageDependencies": [\ + ["bson", "npm:5.4.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["buffer", [\ ["npm:5.7.1", {\ "packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-e2cf8429e1.zip/node_modules/buffer/",\ @@ -3374,6 +4723,24 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["buffer-crc32", [\ + ["npm:0.2.13", {\ + "packageLocation": "./.yarn/cache/buffer-crc32-npm-0.2.13-c4b6fceac1-06252347ae.zip/node_modules/buffer-crc32/",\ + "packageDependencies": [\ + ["buffer-crc32", "npm:0.2.13"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["buffer-equal-constant-time", [\ + ["npm:1.0.1", {\ + "packageLocation": "./.yarn/cache/buffer-equal-constant-time-npm-1.0.1-41826f3419-80bb945f5d.zip/node_modules/buffer-equal-constant-time/",\ + "packageDependencies": [\ + ["buffer-equal-constant-time", "npm:1.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["buffer-from", [\ ["npm:1.1.2", {\ "packageLocation": "./.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip/node_modules/buffer-from/",\ @@ -3570,6 +4937,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["class-transformer", [\ + ["npm:0.5.1", {\ + "packageLocation": "./.yarn/cache/class-transformer-npm-0.5.1-96b5161e6c-f191c8b4cc.zip/node_modules/class-transformer/",\ + "packageDependencies": [\ + ["class-transformer", "npm:0.5.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["class-validator", [\ + ["npm:0.14.0", {\ + "packageLocation": "./.yarn/cache/class-validator-npm-0.14.0-b600d5fe72-f62e4a0ad2.zip/node_modules/class-validator/",\ + "packageDependencies": [\ + ["class-validator", "npm:0.14.0"],\ + ["@types/validator", "npm:13.7.17"],\ + ["libphonenumber-js", "npm:1.10.37"],\ + ["validator", "npm:13.9.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["clean-stack", [\ ["npm:2.2.0", {\ "packageLocation": "./.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-2ac8cd2b2f.zip/node_modules/clean-stack/",\ @@ -3740,6 +5128,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["commondir", [\ + ["npm:1.0.1", {\ + "packageLocation": "./.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip/node_modules/commondir/",\ + "packageDependencies": [\ + ["commondir", "npm:1.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["component-emitter", [\ ["npm:1.3.0", {\ "packageLocation": "./.yarn/cache/component-emitter-npm-1.3.0-4b848565b9-b3c46de38f.zip/node_modules/component-emitter/",\ @@ -3825,6 +5222,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }]\ ]],\ ["cookie", [\ + ["npm:0.4.1", {\ + "packageLocation": "./.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip/node_modules/cookie/",\ + "packageDependencies": [\ + ["cookie", "npm:0.4.1"]\ + ],\ + "linkType": "HARD"\ + }],\ ["npm:0.5.0", {\ "packageLocation": "./.yarn/cache/cookie-npm-0.5.0-e2d58a161a-1f4bd2ca57.zip/node_modules/cookie/",\ "packageDependencies": [\ @@ -3833,6 +5237,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["cookie-parser", [\ + ["npm:1.4.6", {\ + "packageLocation": "./.yarn/cache/cookie-parser-npm-1.4.6-a68f84d02a-1e5a63aa82.zip/node_modules/cookie-parser/",\ + "packageDependencies": [\ + ["cookie-parser", "npm:1.4.6"],\ + ["cookie", "npm:0.4.1"],\ + ["cookie-signature", "npm:1.0.6"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["cookie-signature", [\ ["npm:1.0.6", {\ "packageLocation": "./.yarn/cache/cookie-signature-npm-1.0.6-93f325f7f0-f4e1b0a98a.zip/node_modules/cookie-signature/",\ @@ -3906,6 +5321,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["crypto-js", [\ + ["npm:4.1.1", {\ + "packageLocation": "./.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip/node_modules/crypto-js/",\ + "packageDependencies": [\ + ["crypto-js", "npm:4.1.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["debug", [\ ["npm:2.6.9", {\ "packageLocation": "./.yarn/cache/debug-npm-2.6.9-7d4cb597dc-d2f51589ca.zip/node_modules/debug/",\ @@ -4154,6 +5578,24 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["dotenv", [\ + ["npm:16.1.4", {\ + "packageLocation": "./.yarn/cache/dotenv-npm-16.1.4-c4499a21eb-c1b2e13df4.zip/node_modules/dotenv/",\ + "packageDependencies": [\ + ["dotenv", "npm:16.1.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["dotenv-expand", [\ + ["npm:10.0.0", {\ + "packageLocation": "./.yarn/cache/dotenv-expand-npm-10.0.0-fa5b032ad9-2a38b470ef.zip/node_modules/dotenv-expand/",\ + "packageDependencies": [\ + ["dotenv-expand", "npm:10.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["eastasianwidth", [\ ["npm:0.2.0", {\ "packageLocation": "./.yarn/cache/eastasianwidth-npm-0.2.0-c37eb16bd1-7d00d7cd8e.zip/node_modules/eastasianwidth/",\ @@ -4163,6 +5605,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["ecdsa-sig-formatter", [\ + ["npm:1.0.11", {\ + "packageLocation": "./.yarn/cache/ecdsa-sig-formatter-npm-1.0.11-b6784e7852-207f9ab1c2.zip/node_modules/ecdsa-sig-formatter/",\ + "packageDependencies": [\ + ["ecdsa-sig-formatter", "npm:1.0.11"],\ + ["safe-buffer", "npm:5.2.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["ee-first", [\ ["npm:1.1.1", {\ "packageLocation": "./.yarn/cache/ee-first-npm-1.1.1-33f8535b39-1b4cac778d.zip/node_modules/ee-first/",\ @@ -4964,6 +6416,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["fast-xml-parser", [\ + ["npm:4.2.5", {\ + "packageLocation": "./.yarn/cache/fast-xml-parser-npm-4.2.5-342a3689c5-d32b220055.zip/node_modules/fast-xml-parser/",\ + "packageDependencies": [\ + ["fast-xml-parser", "npm:4.2.5"],\ + ["strnum", "npm:1.0.5"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["fastq", [\ ["npm:1.15.0", {\ "packageLocation": "./.yarn/cache/fastq-npm-1.15.0-1013f6514e-0170e6bfcd.zip/node_modules/fastq/",\ @@ -4984,6 +6446,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["fd-slicer", [\ + ["npm:1.1.0", {\ + "packageLocation": "./.yarn/cache/fd-slicer-npm-1.1.0-3cade0050a-c8585fd571.zip/node_modules/fd-slicer/",\ + "packageDependencies": [\ + ["fd-slicer", "npm:1.1.0"],\ + ["pend", "npm:1.2.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["figures", [\ ["npm:3.2.0", {\ "packageLocation": "./.yarn/cache/figures-npm-3.2.0-85d357e955-85a6ad29e9.zip/node_modules/figures/",\ @@ -5030,6 +6502,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["find-cache-dir", [\ + ["npm:3.3.2", {\ + "packageLocation": "./.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip/node_modules/find-cache-dir/",\ + "packageDependencies": [\ + ["find-cache-dir", "npm:3.3.2"],\ + ["commondir", "npm:1.0.1"],\ + ["make-dir", "npm:3.1.0"],\ + ["pkg-dir", "npm:4.2.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["find-up", [\ ["npm:4.1.0", {\ "packageLocation": "./.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-4c172680e8.zip/node_modules/find-up/",\ @@ -5065,7 +6549,29 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["npm:3.2.7", {\ "packageLocation": "./.yarn/cache/flatted-npm-3.2.7-0da10b7c56-427633049d.zip/node_modules/flatted/",\ "packageDependencies": [\ - ["flatted", "npm:3.2.7"]\ + ["flatted", "npm:3.2.7"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["follow-redirects", [\ + ["npm:1.15.2", {\ + "packageLocation": "./.yarn/cache/follow-redirects-npm-1.15.2-1ec1dd82be-faa66059b6.zip/node_modules/follow-redirects/",\ + "packageDependencies": [\ + ["follow-redirects", "npm:1.15.2"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:4d7ce8ca3e1e44d82523fba2ad95e1be18c4e9f8dec6d551377587540da3ed75bd8bd3e812280309a3b90cfdb0560f076f3552a20839f7f15665207a4fbd588a#npm:1.15.2", {\ + "packageLocation": "./.yarn/__virtual__/follow-redirects-virtual-359bc4c55c/0/cache/follow-redirects-npm-1.15.2-1ec1dd82be-faa66059b6.zip/node_modules/follow-redirects/",\ + "packageDependencies": [\ + ["follow-redirects", "virtual:4d7ce8ca3e1e44d82523fba2ad95e1be18c4e9f8dec6d551377587540da3ed75bd8bd3e812280309a3b90cfdb0560f076f3552a20839f7f15665207a4fbd588a#npm:1.15.2"],\ + ["@types/debug", null],\ + ["debug", null]\ + ],\ + "packagePeers": [\ + "@types/debug",\ + "debug"\ ],\ "linkType": "HARD"\ }]\ @@ -5172,6 +6678,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["fs-constants", [\ + ["npm:1.0.0", {\ + "packageLocation": "./.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip/node_modules/fs-constants/",\ + "packageDependencies": [\ + ["fs-constants", "npm:1.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["fs-extra", [\ ["npm:10.1.0", {\ "packageLocation": "./.yarn/cache/fs-extra-npm-10.1.0-86573680ed-dc94ab3709.zip/node_modules/fs-extra/",\ @@ -5318,6 +6833,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["get-port", [\ + ["npm:5.1.1", {\ + "packageLocation": "./.yarn/cache/get-port-npm-5.1.1-2f6074007a-0162663ffe.zip/node_modules/get-port/",\ + "packageDependencies": [\ + ["get-port", "npm:5.1.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["get-stream", [\ ["npm:5.2.0", {\ "packageLocation": "./.yarn/cache/get-stream-npm-5.2.0-2cfd3b452b-8bc1a23174.zip/node_modules/get-stream/",\ @@ -6915,6 +8439,62 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["jsonwebtoken", [\ + ["npm:9.0.0", {\ + "packageLocation": "./.yarn/cache/jsonwebtoken-npm-9.0.0-36fd1594c0-b9181cecf9.zip/node_modules/jsonwebtoken/",\ + "packageDependencies": [\ + ["jsonwebtoken", "npm:9.0.0"],\ + ["jws", "npm:3.2.2"],\ + ["lodash", "npm:4.17.21"],\ + ["ms", "npm:2.1.3"],\ + ["semver", "npm:7.5.4"]\ + ],\ + "linkType": "HARD"\ + }],\ + ["npm:9.0.1", {\ + "packageLocation": "./.yarn/cache/jsonwebtoken-npm-9.0.1-30d1a69741-0eafe26889.zip/node_modules/jsonwebtoken/",\ + "packageDependencies": [\ + ["jsonwebtoken", "npm:9.0.1"],\ + ["jws", "npm:3.2.2"],\ + ["lodash", "npm:4.17.21"],\ + ["ms", "npm:2.1.3"],\ + ["semver", "npm:7.5.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["jwa", [\ + ["npm:1.4.1", {\ + "packageLocation": "./.yarn/cache/jwa-npm-1.4.1-4f19d6572c-ff30ea7c2d.zip/node_modules/jwa/",\ + "packageDependencies": [\ + ["jwa", "npm:1.4.1"],\ + ["buffer-equal-constant-time", "npm:1.0.1"],\ + ["ecdsa-sig-formatter", "npm:1.0.11"],\ + ["safe-buffer", "npm:5.2.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["jws", [\ + ["npm:3.2.2", {\ + "packageLocation": "./.yarn/cache/jws-npm-3.2.2-c1ae59c7af-f0213fe5b7.zip/node_modules/jws/",\ + "packageDependencies": [\ + ["jws", "npm:3.2.2"],\ + ["jwa", "npm:1.4.1"],\ + ["safe-buffer", "npm:5.2.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["kareem", [\ + ["npm:2.5.1", {\ + "packageLocation": "./.yarn/cache/kareem-npm-2.5.1-79134fb43e-b019a960a7.zip/node_modules/kareem/",\ + "packageDependencies": [\ + ["kareem", "npm:2.5.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["kleur", [\ ["npm:3.0.3", {\ "packageLocation": "./.yarn/cache/kleur-npm-3.0.3-f6f53649a4-df82cd1e17.zip/node_modules/kleur/",\ @@ -6944,6 +8524,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["libphonenumber-js", [\ + ["npm:1.10.37", {\ + "packageLocation": "./.yarn/cache/libphonenumber-js-npm-1.10.37-79b0943385-c28f762322.zip/node_modules/libphonenumber-js/",\ + "packageDependencies": [\ + ["libphonenumber-js", "npm:1.10.37"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["lines-and-columns", [\ ["npm:1.2.4", {\ "packageLocation": "./.yarn/cache/lines-and-columns-npm-1.2.4-d6c7cc5799-0c37f9f7fa.zip/node_modules/lines-and-columns/",\ @@ -7122,6 +8711,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["md5-file", [\ + ["npm:5.0.0", {\ + "packageLocation": "./.yarn/cache/md5-file-npm-5.0.0-e5f59abc62-c606a00ff5.zip/node_modules/md5-file/",\ + "packageDependencies": [\ + ["md5-file", "npm:5.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["media-typer", [\ ["npm:0.3.0", {\ "packageLocation": "./.yarn/cache/media-typer-npm-0.3.0-8674f8f0f5-af1b38516c.zip/node_modules/media-typer/",\ @@ -7141,6 +8739,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["memory-pager", [\ + ["npm:1.5.0", {\ + "packageLocation": "./.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip/node_modules/memory-pager/",\ + "packageDependencies": [\ + ["memory-pager", "npm:1.5.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["merge-descriptors", [\ ["npm:1.0.1", {\ "packageLocation": "./.yarn/cache/merge-descriptors-npm-1.0.1-615287aaa8-5abc259d2a.zip/node_modules/merge-descriptors/",\ @@ -7386,6 +8993,132 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["mongodb", [\ + ["npm:4.16.0", {\ + "packageLocation": "./.yarn/cache/mongodb-npm-4.16.0-f6d128b4ab-f0b1347739.zip/node_modules/mongodb/",\ + "packageDependencies": [\ + ["mongodb", "npm:4.16.0"],\ + ["@aws-sdk/credential-providers", "npm:3.370.0"],\ + ["bson", "npm:4.7.2"],\ + ["mongodb-connection-string-url", "npm:2.6.0"],\ + ["saslprep", "npm:1.0.3"],\ + ["socks", "npm:2.7.1"]\ + ],\ + "linkType": "HARD"\ + }],\ + ["npm:5.6.0", {\ + "packageLocation": "./.yarn/cache/mongodb-npm-5.6.0-d7404fc735-5b1594b247.zip/node_modules/mongodb/",\ + "packageDependencies": [\ + ["mongodb", "npm:5.6.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:c1408e44ec38debb371a67c3cacc6e4c8b69ae151ebd9f6f85533556815c6318d135299bea342fd57214f0a62f4c9e1c100bcc853cadb86138d70001ff427f97#npm:5.6.0", {\ + "packageLocation": "./.yarn/__virtual__/mongodb-virtual-5b4b34f00b/0/cache/mongodb-npm-5.6.0-d7404fc735-5b1594b247.zip/node_modules/mongodb/",\ + "packageDependencies": [\ + ["mongodb", "virtual:c1408e44ec38debb371a67c3cacc6e4c8b69ae151ebd9f6f85533556815c6318d135299bea342fd57214f0a62f4c9e1c100bcc853cadb86138d70001ff427f97#npm:5.6.0"],\ + ["@aws-sdk/credential-providers", null],\ + ["@types/aws-sdk__credential-providers", null],\ + ["@types/mongodb-client-encryption", null],\ + ["@types/snappy", null],\ + ["bson", "npm:5.4.0"],\ + ["mongodb-client-encryption", null],\ + ["mongodb-connection-string-url", "npm:2.6.0"],\ + ["saslprep", "npm:1.0.3"],\ + ["snappy", null],\ + ["socks", "npm:2.7.1"]\ + ],\ + "packagePeers": [\ + "@aws-sdk/credential-providers",\ + "@types/aws-sdk__credential-providers",\ + "@types/mongodb-client-encryption",\ + "@types/snappy",\ + "mongodb-client-encryption",\ + "snappy"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["mongodb-connection-string-url", [\ + ["npm:2.6.0", {\ + "packageLocation": "./.yarn/cache/mongodb-connection-string-url-npm-2.6.0-af011ba17f-1d662f0ecf.zip/node_modules/mongodb-connection-string-url/",\ + "packageDependencies": [\ + ["mongodb-connection-string-url", "npm:2.6.0"],\ + ["@types/whatwg-url", "npm:8.2.2"],\ + ["whatwg-url", "npm:11.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["mongodb-memory-server", [\ + ["npm:8.13.0", {\ + "packageLocation": "./.yarn/unplugged/mongodb-memory-server-npm-8.13.0-8ab039ac60/node_modules/mongodb-memory-server/",\ + "packageDependencies": [\ + ["mongodb-memory-server", "npm:8.13.0"],\ + ["mongodb-memory-server-core", "npm:8.13.0"],\ + ["tslib", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["mongodb-memory-server-core", [\ + ["npm:8.13.0", {\ + "packageLocation": "./.yarn/cache/mongodb-memory-server-core-npm-8.13.0-c46ab8f8fc-05ee432d70.zip/node_modules/mongodb-memory-server-core/",\ + "packageDependencies": [\ + ["mongodb-memory-server-core", "npm:8.13.0"],\ + ["async-mutex", "npm:0.3.2"],\ + ["camelcase", "npm:6.3.0"],\ + ["debug", "virtual:352060d453a801a0fd16b073afb56a2607e33f96a5ff7faf9351853a737843ea6ad854442954d36ef1f89461751ed94f005ea67c746b39723d1da041a244f7d2#npm:4.3.4"],\ + ["find-cache-dir", "npm:3.3.2"],\ + ["get-port", "npm:5.1.1"],\ + ["https-proxy-agent", "npm:5.0.1"],\ + ["md5-file", "npm:5.0.0"],\ + ["mongodb", "npm:4.16.0"],\ + ["new-find-package-json", "npm:2.0.0"],\ + ["semver", "npm:7.5.4"],\ + ["tar-stream", "npm:2.2.0"],\ + ["tslib", "npm:2.6.0"],\ + ["uuid", "npm:9.0.0"],\ + ["yauzl", "npm:2.10.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["mongoose", [\ + ["npm:7.3.4", {\ + "packageLocation": "./.yarn/cache/mongoose-npm-7.3.4-c1408e44ec-1ffdb8449c.zip/node_modules/mongoose/",\ + "packageDependencies": [\ + ["mongoose", "npm:7.3.4"],\ + ["bson", "npm:5.4.0"],\ + ["kareem", "npm:2.5.1"],\ + ["mongodb", "virtual:c1408e44ec38debb371a67c3cacc6e4c8b69ae151ebd9f6f85533556815c6318d135299bea342fd57214f0a62f4c9e1c100bcc853cadb86138d70001ff427f97#npm:5.6.0"],\ + ["mpath", "npm:0.9.0"],\ + ["mquery", "npm:5.0.0"],\ + ["ms", "npm:2.1.3"],\ + ["sift", "npm:16.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["mpath", [\ + ["npm:0.9.0", {\ + "packageLocation": "./.yarn/cache/mpath-npm-0.9.0-e79cc94aea-1052f1f926.zip/node_modules/mpath/",\ + "packageDependencies": [\ + ["mpath", "npm:0.9.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["mquery", [\ + ["npm:5.0.0", {\ + "packageLocation": "./.yarn/cache/mquery-npm-5.0.0-e714f098ee-0617ead71e.zip/node_modules/mquery/",\ + "packageDependencies": [\ + ["mquery", "npm:5.0.0"],\ + ["debug", "virtual:352060d453a801a0fd16b073afb56a2607e33f96a5ff7faf9351853a737843ea6ad854442954d36ef1f89461751ed94f005ea67c746b39723d1da041a244f7d2#npm:4.3.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["ms", [\ ["npm:2.0.0", {\ "packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-0e6a22b8b7.zip/node_modules/ms/",\ @@ -7423,6 +9156,20 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["xtend", "npm:4.0.2"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:1.4.5-lts.1", {\ + "packageLocation": "./.yarn/cache/multer-npm-1.4.5-lts.1-2b83a2d180-d6dfa78a6e.zip/node_modules/multer/",\ + "packageDependencies": [\ + ["multer", "npm:1.4.5-lts.1"],\ + ["append-field", "npm:1.0.0"],\ + ["busboy", "npm:1.6.0"],\ + ["concat-stream", "npm:1.6.2"],\ + ["mkdirp", "npm:0.5.6"],\ + ["object-assign", "npm:4.1.1"],\ + ["type-is", "npm:1.6.18"],\ + ["xtend", "npm:4.0.2"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["mute-stream", [\ @@ -7470,6 +9217,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["new-find-package-json", [\ + ["npm:2.0.0", {\ + "packageLocation": "./.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip/node_modules/new-find-package-json/",\ + "packageDependencies": [\ + ["new-find-package-json", "npm:2.0.0"],\ + ["debug", "virtual:352060d453a801a0fd16b073afb56a2607e33f96a5ff7faf9351853a737843ea6ad854442954d36ef1f89461751ed94f005ea67c746b39723d1da041a244f7d2#npm:4.3.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["node-abort-controller", [\ ["npm:3.1.1", {\ "packageLocation": "./.yarn/cache/node-abort-controller-npm-3.1.1-e246ed42cd-2c340916af.zip/node_modules/node-abort-controller/",\ @@ -7843,6 +9600,48 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["passport", [\ + ["npm:0.6.0", {\ + "packageLocation": "./.yarn/cache/passport-npm-0.6.0-0a57682d5b-ef932ad671.zip/node_modules/passport/",\ + "packageDependencies": [\ + ["passport", "npm:0.6.0"],\ + ["passport-strategy", "npm:1.0.0"],\ + ["pause", "npm:0.0.1"],\ + ["utils-merge", "npm:1.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["passport-jwt", [\ + ["npm:4.0.1", {\ + "packageLocation": "./.yarn/cache/passport-jwt-npm-4.0.1-2841eea09e-0669d5bf8f.zip/node_modules/passport-jwt/",\ + "packageDependencies": [\ + ["passport-jwt", "npm:4.0.1"],\ + ["jsonwebtoken", "npm:9.0.1"],\ + ["passport-strategy", "npm:1.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["passport-local", [\ + ["npm:1.0.0", {\ + "packageLocation": "./.yarn/cache/passport-local-npm-1.0.0-ed89961a0c-86dc08b12f.zip/node_modules/passport-local/",\ + "packageDependencies": [\ + ["passport-local", "npm:1.0.0"],\ + ["passport-strategy", "npm:1.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["passport-strategy", [\ + ["npm:1.0.0", {\ + "packageLocation": "./.yarn/cache/passport-strategy-npm-1.0.0-5648a82a66-5086693f25.zip/node_modules/passport-strategy/",\ + "packageDependencies": [\ + ["passport-strategy", "npm:1.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["path-exists", [\ ["npm:4.0.0", {\ "packageLocation": "./.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-505807199d.zip/node_modules/path-exists/",\ @@ -7922,6 +9721,24 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["pause", [\ + ["npm:0.0.1", {\ + "packageLocation": "./.yarn/cache/pause-npm-0.0.1-0c421a299d-e96ee581b6.zip/node_modules/pause/",\ + "packageDependencies": [\ + ["pause", "npm:0.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["pend", [\ + ["npm:1.2.0", {\ + "packageLocation": "./.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip/node_modules/pend/",\ + "packageDependencies": [\ + ["pend", "npm:1.2.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["picocolors", [\ ["npm:1.0.0", {\ "packageLocation": "./.yarn/cache/picocolors-npm-1.0.0-d81e0b1927-a2e8092dd8.zip/node_modules/picocolors/",\ @@ -8050,6 +9867,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["proxy-from-env", [\ + ["npm:1.1.0", {\ + "packageLocation": "./.yarn/cache/proxy-from-env-npm-1.1.0-c13d07f26b-ed7fcc2ba0.zip/node_modules/proxy-from-env/",\ + "packageDependencies": [\ + ["proxy-from-env", "npm:1.1.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["pump", [\ ["npm:3.0.0", {\ "packageLocation": "./.yarn/cache/pump-npm-3.0.0-0080bf6a7a-e42e9229fb.zip/node_modules/pump/",\ @@ -8431,6 +10257,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["saslprep", [\ + ["npm:1.0.3", {\ + "packageLocation": "./.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip/node_modules/saslprep/",\ + "packageDependencies": [\ + ["saslprep", "npm:1.0.3"],\ + ["sparse-bitfield", "npm:3.0.3"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["schema-utils", [\ ["npm:3.3.0", {\ "packageLocation": "./.yarn/cache/schema-utils-npm-3.3.0-f2b36937f1-ea56971926.zip/node_modules/schema-utils/",\ @@ -8566,6 +10402,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["sift", [\ + ["npm:16.0.1", {\ + "packageLocation": "./.yarn/cache/sift-npm-16.0.1-ad548f4923-5fe18a517a.zip/node_modules/sift/",\ + "packageDependencies": [\ + ["sift", "npm:16.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["signal-exit", [\ ["npm:3.0.7", {\ "packageLocation": "./.yarn/cache/signal-exit-npm-3.0.7-bd270458a3-a2f098f247.zip/node_modules/signal-exit/",\ @@ -8675,6 +10520,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["sparse-bitfield", [\ + ["npm:3.0.3", {\ + "packageLocation": "./.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip/node_modules/sparse-bitfield/",\ + "packageDependencies": [\ + ["sparse-bitfield", "npm:3.0.3"],\ + ["memory-pager", "npm:1.5.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["sprintf-js", [\ ["npm:1.0.3", {\ "packageLocation": "./.yarn/cache/sprintf-js-npm-1.0.3-73f0a322fa-19d79aec21.zip/node_modules/sprintf-js/",\ @@ -8868,6 +10723,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["strnum", [\ + ["npm:1.0.5", {\ + "packageLocation": "./.yarn/cache/strnum-npm-1.0.5-9ba11d2a0a-651b2031db.zip/node_modules/strnum/",\ + "packageDependencies": [\ + ["strnum", "npm:1.0.5"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["superagent", [\ ["npm:8.0.9", {\ "packageLocation": "./.yarn/cache/superagent-npm-8.0.9-da05128b97-5d00cdc7ce.zip/node_modules/superagent/",\ @@ -8977,6 +10841,20 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["tar-stream", [\ + ["npm:2.2.0", {\ + "packageLocation": "./.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip/node_modules/tar-stream/",\ + "packageDependencies": [\ + ["tar-stream", "npm:2.2.0"],\ + ["bl", "npm:4.1.0"],\ + ["end-of-stream", "npm:1.4.4"],\ + ["fs-constants", "npm:1.0.0"],\ + ["inherits", "npm:2.0.4"],\ + ["readable-stream", "npm:3.6.2"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["terser", [\ ["npm:5.19.0", {\ "packageLocation": "./.yarn/cache/terser-npm-5.19.0-0e40ff70a0-31c937f1a3.zip/node_modules/terser/",\ @@ -9122,6 +11000,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["tr46", "npm:0.0.3"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:3.0.0", {\ + "packageLocation": "./.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-44c3cc6767.zip/node_modules/tr46/",\ + "packageDependencies": [\ + ["tr46", "npm:3.0.0"],\ + ["punycode", "npm:2.3.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["tree-kill", [\ @@ -9202,13 +11088,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["micromatch", "npm:4.0.5"],\ ["semver", "npm:7.5.4"],\ ["typescript", "patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071"],\ - ["webpack", null]\ + ["webpack", "virtual:a457ff59f2815d4b942f0c138b8fcd2309c2207bd1dfea2a0b20d11af769b32d0e35b7ebf49f5eade4aa36a3ef97609ed150b9ae60aff68c5742bd9db99c0102#npm:5.88.1"]\ ],\ "packagePeers": [\ "@types/typescript",\ "@types/webpack",\ - "typescript",\ - "webpack"\ + "typescript"\ ],\ "linkType": "HARD"\ }]\ @@ -9558,6 +11443,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["uuid", [\ + ["npm:8.3.2", {\ + "packageLocation": "./.yarn/cache/uuid-npm-8.3.2-eca0baba53-5575a8a75c.zip/node_modules/uuid/",\ + "packageDependencies": [\ + ["uuid", "npm:8.3.2"]\ + ],\ + "linkType": "HARD"\ + }],\ + ["npm:9.0.0", {\ + "packageLocation": "./.yarn/cache/uuid-npm-9.0.0-46c41e3e43-8dd2c83c43.zip/node_modules/uuid/",\ + "packageDependencies": [\ + ["uuid", "npm:9.0.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["v8-compile-cache-lib", [\ ["npm:3.0.1", {\ "packageLocation": "./.yarn/cache/v8-compile-cache-lib-npm-3.0.1-4886071ece-78089ad549.zip/node_modules/v8-compile-cache-lib/",\ @@ -9579,6 +11480,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["validator", [\ + ["npm:13.9.0", {\ + "packageLocation": "./.yarn/cache/validator-npm-13.9.0-54b07e9e81-e2c936f041.zip/node_modules/validator/",\ + "packageDependencies": [\ + ["validator", "npm:13.9.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["vary", [\ ["npm:1.1.2", {\ "packageLocation": "./.yarn/cache/vary-npm-1.1.2-b49f70ae63-ae0123222c.zip/node_modules/vary/",\ @@ -9626,6 +11536,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["webidl-conversions", "npm:3.0.1"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:7.0.0", {\ + "packageLocation": "./.yarn/cache/webidl-conversions-npm-7.0.0-e8c8e30c68-f05588567a.zip/node_modules/webidl-conversions/",\ + "packageDependencies": [\ + ["webidl-conversions", "npm:7.0.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["webpack", [\ @@ -9693,6 +11610,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }]\ ]],\ ["whatwg-url", [\ + ["npm:11.0.0", {\ + "packageLocation": "./.yarn/cache/whatwg-url-npm-11.0.0-073529d93a-ed4826aaa5.zip/node_modules/whatwg-url/",\ + "packageDependencies": [\ + ["whatwg-url", "npm:11.0.0"],\ + ["tr46", "npm:3.0.0"],\ + ["webidl-conversions", "npm:7.0.0"]\ + ],\ + "linkType": "HARD"\ + }],\ ["npm:5.0.0", {\ "packageLocation": "./.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-b8daed4ad3.zip/node_modules/whatwg-url/",\ "packageDependencies": [\ @@ -9872,6 +11798,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["yauzl", [\ + ["npm:2.10.0", {\ + "packageLocation": "./.yarn/cache/yauzl-npm-2.10.0-72e70ea021-7f21fe0bba.zip/node_modules/yauzl/",\ + "packageDependencies": [\ + ["yauzl", "npm:2.10.0"],\ + ["buffer-crc32", "npm:0.2.13"],\ + ["fd-slicer", "npm:1.1.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["yn", [\ ["npm:3.1.1", {\ "packageLocation": "./.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip/node_modules/yn/",\ diff --git a/.yarn/cache/@aws-crypto-crc32-npm-3.0.0-10d83e85b0-9fdb3e837f.zip b/.yarn/cache/@aws-crypto-crc32-npm-3.0.0-10d83e85b0-9fdb3e837f.zip new file mode 100644 index 0000000..0b1b7c3 Binary files /dev/null and b/.yarn/cache/@aws-crypto-crc32-npm-3.0.0-10d83e85b0-9fdb3e837f.zip differ diff --git a/.yarn/cache/@aws-crypto-ie11-detection-npm-3.0.0-71f24dcf6a-299b2ddd46.zip b/.yarn/cache/@aws-crypto-ie11-detection-npm-3.0.0-71f24dcf6a-299b2ddd46.zip new file mode 100644 index 0000000..cee7a42 Binary files /dev/null and b/.yarn/cache/@aws-crypto-ie11-detection-npm-3.0.0-71f24dcf6a-299b2ddd46.zip differ diff --git a/.yarn/cache/@aws-crypto-sha256-browser-npm-3.0.0-467f48a447-ca89456bf5.zip b/.yarn/cache/@aws-crypto-sha256-browser-npm-3.0.0-467f48a447-ca89456bf5.zip new file mode 100644 index 0000000..c7ad35f Binary files /dev/null and b/.yarn/cache/@aws-crypto-sha256-browser-npm-3.0.0-467f48a447-ca89456bf5.zip differ diff --git a/.yarn/cache/@aws-crypto-sha256-js-npm-3.0.0-2ba1013fd6-644ded32ea.zip b/.yarn/cache/@aws-crypto-sha256-js-npm-3.0.0-2ba1013fd6-644ded32ea.zip new file mode 100644 index 0000000..7524def Binary files /dev/null and b/.yarn/cache/@aws-crypto-sha256-js-npm-3.0.0-2ba1013fd6-644ded32ea.zip differ diff --git a/.yarn/cache/@aws-crypto-supports-web-crypto-npm-3.0.0-55222d294a-35479a1558.zip b/.yarn/cache/@aws-crypto-supports-web-crypto-npm-3.0.0-55222d294a-35479a1558.zip new file mode 100644 index 0000000..6ae195f Binary files /dev/null and b/.yarn/cache/@aws-crypto-supports-web-crypto-npm-3.0.0-55222d294a-35479a1558.zip differ diff --git a/.yarn/cache/@aws-crypto-util-npm-3.0.0-6c4b38c78e-d29d554504.zip b/.yarn/cache/@aws-crypto-util-npm-3.0.0-6c4b38c78e-d29d554504.zip new file mode 100644 index 0000000..193275a Binary files /dev/null and b/.yarn/cache/@aws-crypto-util-npm-3.0.0-6c4b38c78e-d29d554504.zip differ diff --git a/.yarn/cache/@aws-sdk-client-cognito-identity-npm-3.370.0-9b45c43400-e13eb18f70.zip b/.yarn/cache/@aws-sdk-client-cognito-identity-npm-3.370.0-9b45c43400-e13eb18f70.zip new file mode 100644 index 0000000..e1a4596 Binary files /dev/null and b/.yarn/cache/@aws-sdk-client-cognito-identity-npm-3.370.0-9b45c43400-e13eb18f70.zip differ diff --git a/.yarn/cache/@aws-sdk-client-sso-npm-3.370.0-a9bd886ca2-e6797cac37.zip b/.yarn/cache/@aws-sdk-client-sso-npm-3.370.0-a9bd886ca2-e6797cac37.zip new file mode 100644 index 0000000..cdda093 Binary files /dev/null and b/.yarn/cache/@aws-sdk-client-sso-npm-3.370.0-a9bd886ca2-e6797cac37.zip differ diff --git a/.yarn/cache/@aws-sdk-client-sso-oidc-npm-3.370.0-9943f04319-a94d58fdef.zip b/.yarn/cache/@aws-sdk-client-sso-oidc-npm-3.370.0-9943f04319-a94d58fdef.zip new file mode 100644 index 0000000..4bf8133 Binary files /dev/null and b/.yarn/cache/@aws-sdk-client-sso-oidc-npm-3.370.0-9943f04319-a94d58fdef.zip differ diff --git a/.yarn/cache/@aws-sdk-client-sts-npm-3.370.0-886ff7c162-55ce8a7a8a.zip b/.yarn/cache/@aws-sdk-client-sts-npm-3.370.0-886ff7c162-55ce8a7a8a.zip new file mode 100644 index 0000000..776d426 Binary files /dev/null and b/.yarn/cache/@aws-sdk-client-sts-npm-3.370.0-886ff7c162-55ce8a7a8a.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-cognito-identity-npm-3.370.0-9f621fdb7e-3ee862b115.zip b/.yarn/cache/@aws-sdk-credential-provider-cognito-identity-npm-3.370.0-9f621fdb7e-3ee862b115.zip new file mode 100644 index 0000000..6f13ebc Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-cognito-identity-npm-3.370.0-9f621fdb7e-3ee862b115.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-env-npm-3.370.0-63ab7b9144-0295278ca3.zip b/.yarn/cache/@aws-sdk-credential-provider-env-npm-3.370.0-63ab7b9144-0295278ca3.zip new file mode 100644 index 0000000..1574b82 Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-env-npm-3.370.0-63ab7b9144-0295278ca3.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.370.0-e1030b1253-8d8d0d386e.zip b/.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.370.0-e1030b1253-8d8d0d386e.zip new file mode 100644 index 0000000..f9e7fdc Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.370.0-e1030b1253-8d8d0d386e.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-node-npm-3.370.0-8675fb3d01-564422e77d.zip b/.yarn/cache/@aws-sdk-credential-provider-node-npm-3.370.0-8675fb3d01-564422e77d.zip new file mode 100644 index 0000000..795b8a2 Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-node-npm-3.370.0-8675fb3d01-564422e77d.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-process-npm-3.370.0-5a4d7fd671-286080cfdb.zip b/.yarn/cache/@aws-sdk-credential-provider-process-npm-3.370.0-5a4d7fd671-286080cfdb.zip new file mode 100644 index 0000000..15f04e4 Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-process-npm-3.370.0-5a4d7fd671-286080cfdb.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.370.0-03241fdcc0-a5ef52fd21.zip b/.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.370.0-03241fdcc0-a5ef52fd21.zip new file mode 100644 index 0000000..cb18d71 Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.370.0-03241fdcc0-a5ef52fd21.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.370.0-da1f5214fa-3e9ce1c774.zip b/.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.370.0-da1f5214fa-3e9ce1c774.zip new file mode 100644 index 0000000..b863e41 Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.370.0-da1f5214fa-3e9ce1c774.zip differ diff --git a/.yarn/cache/@aws-sdk-credential-providers-npm-3.370.0-6591d3aac1-7eb60bda6b.zip b/.yarn/cache/@aws-sdk-credential-providers-npm-3.370.0-6591d3aac1-7eb60bda6b.zip new file mode 100644 index 0000000..1cd6bb5 Binary files /dev/null and b/.yarn/cache/@aws-sdk-credential-providers-npm-3.370.0-6591d3aac1-7eb60bda6b.zip differ diff --git a/.yarn/cache/@aws-sdk-middleware-host-header-npm-3.370.0-874d1a2ffd-2a7d3f3a0a.zip b/.yarn/cache/@aws-sdk-middleware-host-header-npm-3.370.0-874d1a2ffd-2a7d3f3a0a.zip new file mode 100644 index 0000000..340e76c Binary files /dev/null and b/.yarn/cache/@aws-sdk-middleware-host-header-npm-3.370.0-874d1a2ffd-2a7d3f3a0a.zip differ diff --git a/.yarn/cache/@aws-sdk-middleware-logger-npm-3.370.0-12a9f2b942-f3c4062247.zip b/.yarn/cache/@aws-sdk-middleware-logger-npm-3.370.0-12a9f2b942-f3c4062247.zip new file mode 100644 index 0000000..a39328e Binary files /dev/null and b/.yarn/cache/@aws-sdk-middleware-logger-npm-3.370.0-12a9f2b942-f3c4062247.zip differ diff --git a/.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.370.0-914f181981-ee25951954.zip b/.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.370.0-914f181981-ee25951954.zip new file mode 100644 index 0000000..b6abb21 Binary files /dev/null and b/.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.370.0-914f181981-ee25951954.zip differ diff --git a/.yarn/cache/@aws-sdk-middleware-sdk-sts-npm-3.370.0-ebc73af313-14877966d5.zip b/.yarn/cache/@aws-sdk-middleware-sdk-sts-npm-3.370.0-ebc73af313-14877966d5.zip new file mode 100644 index 0000000..3aaf653 Binary files /dev/null and b/.yarn/cache/@aws-sdk-middleware-sdk-sts-npm-3.370.0-ebc73af313-14877966d5.zip differ diff --git a/.yarn/cache/@aws-sdk-middleware-signing-npm-3.370.0-2cb6d89c62-f8581ad377.zip b/.yarn/cache/@aws-sdk-middleware-signing-npm-3.370.0-2cb6d89c62-f8581ad377.zip new file mode 100644 index 0000000..17a3991 Binary files /dev/null and b/.yarn/cache/@aws-sdk-middleware-signing-npm-3.370.0-2cb6d89c62-f8581ad377.zip differ diff --git a/.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.370.0-179c9f8167-c4366db10a.zip b/.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.370.0-179c9f8167-c4366db10a.zip new file mode 100644 index 0000000..33f14b4 Binary files /dev/null and b/.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.370.0-179c9f8167-c4366db10a.zip differ diff --git a/.yarn/cache/@aws-sdk-token-providers-npm-3.370.0-a1ea87e333-7126c5bdc8.zip b/.yarn/cache/@aws-sdk-token-providers-npm-3.370.0-a1ea87e333-7126c5bdc8.zip new file mode 100644 index 0000000..11d9f24 Binary files /dev/null and b/.yarn/cache/@aws-sdk-token-providers-npm-3.370.0-a1ea87e333-7126c5bdc8.zip differ diff --git a/.yarn/cache/@aws-sdk-types-npm-3.370.0-25e9f06cca-105a5768f2.zip b/.yarn/cache/@aws-sdk-types-npm-3.370.0-25e9f06cca-105a5768f2.zip new file mode 100644 index 0000000..f076da3 Binary files /dev/null and b/.yarn/cache/@aws-sdk-types-npm-3.370.0-25e9f06cca-105a5768f2.zip differ diff --git a/.yarn/cache/@aws-sdk-util-endpoints-npm-3.370.0-a26911f59e-d351ad2fdc.zip b/.yarn/cache/@aws-sdk-util-endpoints-npm-3.370.0-a26911f59e-d351ad2fdc.zip new file mode 100644 index 0000000..102e7cb Binary files /dev/null and b/.yarn/cache/@aws-sdk-util-endpoints-npm-3.370.0-a26911f59e-d351ad2fdc.zip differ diff --git a/.yarn/cache/@aws-sdk-util-locate-window-npm-3.310.0-0bb775a2bf-d552ce5f0f.zip b/.yarn/cache/@aws-sdk-util-locate-window-npm-3.310.0-0bb775a2bf-d552ce5f0f.zip new file mode 100644 index 0000000..b2e6462 Binary files /dev/null and b/.yarn/cache/@aws-sdk-util-locate-window-npm-3.310.0-0bb775a2bf-d552ce5f0f.zip differ diff --git a/.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.370.0-15a59b38ca-3a549b1337.zip b/.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.370.0-15a59b38ca-3a549b1337.zip new file mode 100644 index 0000000..790afdf Binary files /dev/null and b/.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.370.0-15a59b38ca-3a549b1337.zip differ diff --git a/.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.370.0-1bd47fb171-83b1f2c7f2.zip b/.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.370.0-1bd47fb171-83b1f2c7f2.zip new file mode 100644 index 0000000..1ce84ac Binary files /dev/null and b/.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.370.0-1bd47fb171-83b1f2c7f2.zip differ diff --git a/.yarn/cache/@aws-sdk-util-utf8-browser-npm-3.259.0-343a1dba08-b6a1e580da.zip b/.yarn/cache/@aws-sdk-util-utf8-browser-npm-3.259.0-343a1dba08-b6a1e580da.zip new file mode 100644 index 0000000..81ac20a Binary files /dev/null and b/.yarn/cache/@aws-sdk-util-utf8-browser-npm-3.259.0-343a1dba08-b6a1e580da.zip differ diff --git a/.yarn/cache/@nestjs-axios-npm-3.0.0-a8fe50b1d1-0483ef5a10.zip b/.yarn/cache/@nestjs-axios-npm-3.0.0-a8fe50b1d1-0483ef5a10.zip new file mode 100644 index 0000000..8ccca2c Binary files /dev/null and b/.yarn/cache/@nestjs-axios-npm-3.0.0-a8fe50b1d1-0483ef5a10.zip differ diff --git a/.yarn/cache/@nestjs-config-npm-3.0.0-e214dbc65d-e886dc337c.zip b/.yarn/cache/@nestjs-config-npm-3.0.0-e214dbc65d-e886dc337c.zip new file mode 100644 index 0000000..4973dd7 Binary files /dev/null and b/.yarn/cache/@nestjs-config-npm-3.0.0-e214dbc65d-e886dc337c.zip differ diff --git a/.yarn/cache/@nestjs-jwt-npm-10.1.0-3d70367215-c172b11ce1.zip b/.yarn/cache/@nestjs-jwt-npm-10.1.0-3d70367215-c172b11ce1.zip new file mode 100644 index 0000000..185dbce Binary files /dev/null and b/.yarn/cache/@nestjs-jwt-npm-10.1.0-3d70367215-c172b11ce1.zip differ diff --git a/.yarn/cache/@nestjs-mapped-types-npm-2.0.2-3b001953e2-1fb0dca383.zip b/.yarn/cache/@nestjs-mapped-types-npm-2.0.2-3b001953e2-1fb0dca383.zip new file mode 100644 index 0000000..c467c2f Binary files /dev/null and b/.yarn/cache/@nestjs-mapped-types-npm-2.0.2-3b001953e2-1fb0dca383.zip differ diff --git a/.yarn/cache/@nestjs-mongoose-npm-10.0.0-0bac3c9c0e-faf75fdc24.zip b/.yarn/cache/@nestjs-mongoose-npm-10.0.0-0bac3c9c0e-faf75fdc24.zip new file mode 100644 index 0000000..e70d73a Binary files /dev/null and b/.yarn/cache/@nestjs-mongoose-npm-10.0.0-0bac3c9c0e-faf75fdc24.zip differ diff --git a/.yarn/cache/@nestjs-passport-npm-10.0.0-c1f99da0b6-5443695832.zip b/.yarn/cache/@nestjs-passport-npm-10.0.0-c1f99da0b6-5443695832.zip new file mode 100644 index 0000000..5c05a56 Binary files /dev/null and b/.yarn/cache/@nestjs-passport-npm-10.0.0-c1f99da0b6-5443695832.zip differ diff --git a/.yarn/cache/@nestjs-testing-npm-10.0.5-7ab4fac79c-dfc6b492ca.zip b/.yarn/cache/@nestjs-testing-npm-10.1.0-fefe10019d-80e609e4d0.zip similarity index 66% rename from .yarn/cache/@nestjs-testing-npm-10.0.5-7ab4fac79c-dfc6b492ca.zip rename to .yarn/cache/@nestjs-testing-npm-10.1.0-fefe10019d-80e609e4d0.zip index c39913e..c161cd8 100644 Binary files a/.yarn/cache/@nestjs-testing-npm-10.0.5-7ab4fac79c-dfc6b492ca.zip and b/.yarn/cache/@nestjs-testing-npm-10.1.0-fefe10019d-80e609e4d0.zip differ diff --git a/.yarn/cache/@smithy-abort-controller-npm-1.0.2-7f32adc09f-5d0c5b0463.zip b/.yarn/cache/@smithy-abort-controller-npm-1.0.2-7f32adc09f-5d0c5b0463.zip new file mode 100644 index 0000000..8552d59 Binary files /dev/null and b/.yarn/cache/@smithy-abort-controller-npm-1.0.2-7f32adc09f-5d0c5b0463.zip differ diff --git a/.yarn/cache/@smithy-config-resolver-npm-1.0.2-0f50d5f700-23efcb59d6.zip b/.yarn/cache/@smithy-config-resolver-npm-1.0.2-0f50d5f700-23efcb59d6.zip new file mode 100644 index 0000000..e026c26 Binary files /dev/null and b/.yarn/cache/@smithy-config-resolver-npm-1.0.2-0f50d5f700-23efcb59d6.zip differ diff --git a/.yarn/cache/@smithy-credential-provider-imds-npm-1.0.2-08dc132d2c-91b57891b6.zip b/.yarn/cache/@smithy-credential-provider-imds-npm-1.0.2-08dc132d2c-91b57891b6.zip new file mode 100644 index 0000000..1766a85 Binary files /dev/null and b/.yarn/cache/@smithy-credential-provider-imds-npm-1.0.2-08dc132d2c-91b57891b6.zip differ diff --git a/.yarn/cache/@smithy-eventstream-codec-npm-1.0.2-c7c5feddbc-64e2f6e5b0.zip b/.yarn/cache/@smithy-eventstream-codec-npm-1.0.2-c7c5feddbc-64e2f6e5b0.zip new file mode 100644 index 0000000..97b9001 Binary files /dev/null and b/.yarn/cache/@smithy-eventstream-codec-npm-1.0.2-c7c5feddbc-64e2f6e5b0.zip differ diff --git a/.yarn/cache/@smithy-fetch-http-handler-npm-1.0.2-ad97dc7181-62965f69eb.zip b/.yarn/cache/@smithy-fetch-http-handler-npm-1.0.2-ad97dc7181-62965f69eb.zip new file mode 100644 index 0000000..5d2182b Binary files /dev/null and b/.yarn/cache/@smithy-fetch-http-handler-npm-1.0.2-ad97dc7181-62965f69eb.zip differ diff --git a/.yarn/cache/@smithy-hash-node-npm-1.0.2-efde6bd2e2-018ef3271c.zip b/.yarn/cache/@smithy-hash-node-npm-1.0.2-efde6bd2e2-018ef3271c.zip new file mode 100644 index 0000000..52df196 Binary files /dev/null and b/.yarn/cache/@smithy-hash-node-npm-1.0.2-efde6bd2e2-018ef3271c.zip differ diff --git a/.yarn/cache/@smithy-invalid-dependency-npm-1.0.2-c3c4b73575-51d4bfc258.zip b/.yarn/cache/@smithy-invalid-dependency-npm-1.0.2-c3c4b73575-51d4bfc258.zip new file mode 100644 index 0000000..4c30eaa Binary files /dev/null and b/.yarn/cache/@smithy-invalid-dependency-npm-1.0.2-c3c4b73575-51d4bfc258.zip differ diff --git a/.yarn/cache/@smithy-is-array-buffer-npm-1.0.2-a8ef5bb507-811b100b80.zip b/.yarn/cache/@smithy-is-array-buffer-npm-1.0.2-a8ef5bb507-811b100b80.zip new file mode 100644 index 0000000..ff8bef2 Binary files /dev/null and b/.yarn/cache/@smithy-is-array-buffer-npm-1.0.2-a8ef5bb507-811b100b80.zip differ diff --git a/.yarn/cache/@smithy-middleware-content-length-npm-1.0.2-b108132555-4b00aa7411.zip b/.yarn/cache/@smithy-middleware-content-length-npm-1.0.2-b108132555-4b00aa7411.zip new file mode 100644 index 0000000..d250509 Binary files /dev/null and b/.yarn/cache/@smithy-middleware-content-length-npm-1.0.2-b108132555-4b00aa7411.zip differ diff --git a/.yarn/cache/@smithy-middleware-endpoint-npm-1.0.3-81e4f50747-8081b51595.zip b/.yarn/cache/@smithy-middleware-endpoint-npm-1.0.3-81e4f50747-8081b51595.zip new file mode 100644 index 0000000..c673955 Binary files /dev/null and b/.yarn/cache/@smithy-middleware-endpoint-npm-1.0.3-81e4f50747-8081b51595.zip differ diff --git a/.yarn/cache/@smithy-middleware-retry-npm-1.0.4-db58446b68-3ca5c4abe3.zip b/.yarn/cache/@smithy-middleware-retry-npm-1.0.4-db58446b68-3ca5c4abe3.zip new file mode 100644 index 0000000..b89760c Binary files /dev/null and b/.yarn/cache/@smithy-middleware-retry-npm-1.0.4-db58446b68-3ca5c4abe3.zip differ diff --git a/.yarn/cache/@smithy-middleware-serde-npm-1.0.2-9a3fabbdce-563045c0ad.zip b/.yarn/cache/@smithy-middleware-serde-npm-1.0.2-9a3fabbdce-563045c0ad.zip new file mode 100644 index 0000000..73dc9f3 Binary files /dev/null and b/.yarn/cache/@smithy-middleware-serde-npm-1.0.2-9a3fabbdce-563045c0ad.zip differ diff --git a/.yarn/cache/@smithy-middleware-stack-npm-1.0.2-569268495f-34794d1e6d.zip b/.yarn/cache/@smithy-middleware-stack-npm-1.0.2-569268495f-34794d1e6d.zip new file mode 100644 index 0000000..56684b7 Binary files /dev/null and b/.yarn/cache/@smithy-middleware-stack-npm-1.0.2-569268495f-34794d1e6d.zip differ diff --git a/.yarn/cache/@smithy-node-config-provider-npm-1.0.2-b1838ef775-593bd5adf0.zip b/.yarn/cache/@smithy-node-config-provider-npm-1.0.2-b1838ef775-593bd5adf0.zip new file mode 100644 index 0000000..a6e5135 Binary files /dev/null and b/.yarn/cache/@smithy-node-config-provider-npm-1.0.2-b1838ef775-593bd5adf0.zip differ diff --git a/.yarn/cache/@smithy-node-http-handler-npm-1.0.3-e77f20770f-727e1391cc.zip b/.yarn/cache/@smithy-node-http-handler-npm-1.0.3-e77f20770f-727e1391cc.zip new file mode 100644 index 0000000..5e89ab2 Binary files /dev/null and b/.yarn/cache/@smithy-node-http-handler-npm-1.0.3-e77f20770f-727e1391cc.zip differ diff --git a/.yarn/cache/@smithy-property-provider-npm-1.0.2-0958386554-55d09de3da.zip b/.yarn/cache/@smithy-property-provider-npm-1.0.2-0958386554-55d09de3da.zip new file mode 100644 index 0000000..a89e19a Binary files /dev/null and b/.yarn/cache/@smithy-property-provider-npm-1.0.2-0958386554-55d09de3da.zip differ diff --git a/.yarn/cache/@smithy-protocol-http-npm-1.1.1-a6955fb975-6320e8b010.zip b/.yarn/cache/@smithy-protocol-http-npm-1.1.1-a6955fb975-6320e8b010.zip new file mode 100644 index 0000000..d28cf9f Binary files /dev/null and b/.yarn/cache/@smithy-protocol-http-npm-1.1.1-a6955fb975-6320e8b010.zip differ diff --git a/.yarn/cache/@smithy-querystring-builder-npm-1.0.2-cd386247de-c0e6807a5b.zip b/.yarn/cache/@smithy-querystring-builder-npm-1.0.2-cd386247de-c0e6807a5b.zip new file mode 100644 index 0000000..fe80efe Binary files /dev/null and b/.yarn/cache/@smithy-querystring-builder-npm-1.0.2-cd386247de-c0e6807a5b.zip differ diff --git a/.yarn/cache/@smithy-querystring-parser-npm-1.0.2-588300d020-348672b1bc.zip b/.yarn/cache/@smithy-querystring-parser-npm-1.0.2-588300d020-348672b1bc.zip new file mode 100644 index 0000000..9d47660 Binary files /dev/null and b/.yarn/cache/@smithy-querystring-parser-npm-1.0.2-588300d020-348672b1bc.zip differ diff --git a/.yarn/cache/@smithy-service-error-classification-npm-1.0.3-626eea7f1d-adf840de78.zip b/.yarn/cache/@smithy-service-error-classification-npm-1.0.3-626eea7f1d-adf840de78.zip new file mode 100644 index 0000000..86bdd6c Binary files /dev/null and b/.yarn/cache/@smithy-service-error-classification-npm-1.0.3-626eea7f1d-adf840de78.zip differ diff --git a/.yarn/cache/@smithy-shared-ini-file-loader-npm-1.0.2-fe69af7fa4-c9ba908a48.zip b/.yarn/cache/@smithy-shared-ini-file-loader-npm-1.0.2-fe69af7fa4-c9ba908a48.zip new file mode 100644 index 0000000..52b4688 Binary files /dev/null and b/.yarn/cache/@smithy-shared-ini-file-loader-npm-1.0.2-fe69af7fa4-c9ba908a48.zip differ diff --git a/.yarn/cache/@smithy-signature-v4-npm-1.0.2-46dc22d0e0-3e3072ef95.zip b/.yarn/cache/@smithy-signature-v4-npm-1.0.2-46dc22d0e0-3e3072ef95.zip new file mode 100644 index 0000000..96e73a3 Binary files /dev/null and b/.yarn/cache/@smithy-signature-v4-npm-1.0.2-46dc22d0e0-3e3072ef95.zip differ diff --git a/.yarn/cache/@smithy-smithy-client-npm-1.0.4-38923c234a-468aa2f28b.zip b/.yarn/cache/@smithy-smithy-client-npm-1.0.4-38923c234a-468aa2f28b.zip new file mode 100644 index 0000000..2543e93 Binary files /dev/null and b/.yarn/cache/@smithy-smithy-client-npm-1.0.4-38923c234a-468aa2f28b.zip differ diff --git a/.yarn/cache/@smithy-types-npm-1.1.1-77d4550f69-bf4b632eb7.zip b/.yarn/cache/@smithy-types-npm-1.1.1-77d4550f69-bf4b632eb7.zip new file mode 100644 index 0000000..e1c922c Binary files /dev/null and b/.yarn/cache/@smithy-types-npm-1.1.1-77d4550f69-bf4b632eb7.zip differ diff --git a/.yarn/cache/@smithy-url-parser-npm-1.0.2-834c0e5097-1844b23a30.zip b/.yarn/cache/@smithy-url-parser-npm-1.0.2-834c0e5097-1844b23a30.zip new file mode 100644 index 0000000..f443225 Binary files /dev/null and b/.yarn/cache/@smithy-url-parser-npm-1.0.2-834c0e5097-1844b23a30.zip differ diff --git a/.yarn/cache/@smithy-util-base64-npm-1.0.2-233054ecba-618d2952fb.zip b/.yarn/cache/@smithy-util-base64-npm-1.0.2-233054ecba-618d2952fb.zip new file mode 100644 index 0000000..677abe8 Binary files /dev/null and b/.yarn/cache/@smithy-util-base64-npm-1.0.2-233054ecba-618d2952fb.zip differ diff --git a/.yarn/cache/@smithy-util-body-length-browser-npm-1.0.2-dbde19a47e-3b0eacc376.zip b/.yarn/cache/@smithy-util-body-length-browser-npm-1.0.2-dbde19a47e-3b0eacc376.zip new file mode 100644 index 0000000..3a3b6c2 Binary files /dev/null and b/.yarn/cache/@smithy-util-body-length-browser-npm-1.0.2-dbde19a47e-3b0eacc376.zip differ diff --git a/.yarn/cache/@smithy-util-body-length-node-npm-1.0.2-8f10cf41cb-472371dc0a.zip b/.yarn/cache/@smithy-util-body-length-node-npm-1.0.2-8f10cf41cb-472371dc0a.zip new file mode 100644 index 0000000..3801b52 Binary files /dev/null and b/.yarn/cache/@smithy-util-body-length-node-npm-1.0.2-8f10cf41cb-472371dc0a.zip differ diff --git a/.yarn/cache/@smithy-util-buffer-from-npm-1.0.2-58dd9f842b-ca4308ebc5.zip b/.yarn/cache/@smithy-util-buffer-from-npm-1.0.2-58dd9f842b-ca4308ebc5.zip new file mode 100644 index 0000000..2785164 Binary files /dev/null and b/.yarn/cache/@smithy-util-buffer-from-npm-1.0.2-58dd9f842b-ca4308ebc5.zip differ diff --git a/.yarn/cache/@smithy-util-config-provider-npm-1.0.2-d61fd81ad6-625c100b8a.zip b/.yarn/cache/@smithy-util-config-provider-npm-1.0.2-d61fd81ad6-625c100b8a.zip new file mode 100644 index 0000000..e0b60fd Binary files /dev/null and b/.yarn/cache/@smithy-util-config-provider-npm-1.0.2-d61fd81ad6-625c100b8a.zip differ diff --git a/.yarn/cache/@smithy-util-defaults-mode-browser-npm-1.0.2-4be5a50c54-ca03c9f596.zip b/.yarn/cache/@smithy-util-defaults-mode-browser-npm-1.0.2-4be5a50c54-ca03c9f596.zip new file mode 100644 index 0000000..977d3ed Binary files /dev/null and b/.yarn/cache/@smithy-util-defaults-mode-browser-npm-1.0.2-4be5a50c54-ca03c9f596.zip differ diff --git a/.yarn/cache/@smithy-util-defaults-mode-node-npm-1.0.2-e9f11134c0-805977758a.zip b/.yarn/cache/@smithy-util-defaults-mode-node-npm-1.0.2-e9f11134c0-805977758a.zip new file mode 100644 index 0000000..f776f20 Binary files /dev/null and b/.yarn/cache/@smithy-util-defaults-mode-node-npm-1.0.2-e9f11134c0-805977758a.zip differ diff --git a/.yarn/cache/@smithy-util-hex-encoding-npm-1.0.2-701dc5e90a-c70bfccb41.zip b/.yarn/cache/@smithy-util-hex-encoding-npm-1.0.2-701dc5e90a-c70bfccb41.zip new file mode 100644 index 0000000..2bb99f2 Binary files /dev/null and b/.yarn/cache/@smithy-util-hex-encoding-npm-1.0.2-701dc5e90a-c70bfccb41.zip differ diff --git a/.yarn/cache/@smithy-util-middleware-npm-1.0.2-92048f4c53-899aca62ef.zip b/.yarn/cache/@smithy-util-middleware-npm-1.0.2-92048f4c53-899aca62ef.zip new file mode 100644 index 0000000..6908d25 Binary files /dev/null and b/.yarn/cache/@smithy-util-middleware-npm-1.0.2-92048f4c53-899aca62ef.zip differ diff --git a/.yarn/cache/@smithy-util-retry-npm-1.0.4-3e2ede7318-b9a7e464d6.zip b/.yarn/cache/@smithy-util-retry-npm-1.0.4-3e2ede7318-b9a7e464d6.zip new file mode 100644 index 0000000..8ce3a03 Binary files /dev/null and b/.yarn/cache/@smithy-util-retry-npm-1.0.4-3e2ede7318-b9a7e464d6.zip differ diff --git a/.yarn/cache/@smithy-util-stream-npm-1.0.2-4ccf01d6d2-ebea688676.zip b/.yarn/cache/@smithy-util-stream-npm-1.0.2-4ccf01d6d2-ebea688676.zip new file mode 100644 index 0000000..6ce8cb2 Binary files /dev/null and b/.yarn/cache/@smithy-util-stream-npm-1.0.2-4ccf01d6d2-ebea688676.zip differ diff --git a/.yarn/cache/@smithy-util-uri-escape-npm-1.0.2-7a2e0f5e15-6df9fb3aed.zip b/.yarn/cache/@smithy-util-uri-escape-npm-1.0.2-7a2e0f5e15-6df9fb3aed.zip new file mode 100644 index 0000000..c26f39d Binary files /dev/null and b/.yarn/cache/@smithy-util-uri-escape-npm-1.0.2-7a2e0f5e15-6df9fb3aed.zip differ diff --git a/.yarn/cache/@smithy-util-utf8-npm-1.0.2-64fd50c87a-64add10ac1.zip b/.yarn/cache/@smithy-util-utf8-npm-1.0.2-64fd50c87a-64add10ac1.zip new file mode 100644 index 0000000..0528780 Binary files /dev/null and b/.yarn/cache/@smithy-util-utf8-npm-1.0.2-64fd50c87a-64add10ac1.zip differ diff --git a/.yarn/cache/@types-cookie-parser-npm-1.4.3-4bda65954a-f390f3af1b.zip b/.yarn/cache/@types-cookie-parser-npm-1.4.3-4bda65954a-f390f3af1b.zip new file mode 100644 index 0000000..499a404 Binary files /dev/null and b/.yarn/cache/@types-cookie-parser-npm-1.4.3-4bda65954a-f390f3af1b.zip differ diff --git a/.yarn/cache/@types-jsonwebtoken-npm-9.0.2-7af15f7b9b-3bb8d40e78.zip b/.yarn/cache/@types-jsonwebtoken-npm-9.0.2-7af15f7b9b-3bb8d40e78.zip new file mode 100644 index 0000000..9f0c9a9 Binary files /dev/null and b/.yarn/cache/@types-jsonwebtoken-npm-9.0.2-7af15f7b9b-3bb8d40e78.zip differ diff --git a/.yarn/cache/@types-multer-npm-1.4.7-df3819be8e-680cb0710a.zip b/.yarn/cache/@types-multer-npm-1.4.7-df3819be8e-680cb0710a.zip new file mode 100644 index 0000000..98227d5 Binary files /dev/null and b/.yarn/cache/@types-multer-npm-1.4.7-df3819be8e-680cb0710a.zip differ diff --git a/.yarn/cache/@types-validator-npm-13.7.17-200baed5c4-a827e480c0.zip b/.yarn/cache/@types-validator-npm-13.7.17-200baed5c4-a827e480c0.zip new file mode 100644 index 0000000..de7721c Binary files /dev/null and b/.yarn/cache/@types-validator-npm-13.7.17-200baed5c4-a827e480c0.zip differ diff --git a/.yarn/cache/@types-webidl-conversions-npm-7.0.0-0903313151-60142c7ddd.zip b/.yarn/cache/@types-webidl-conversions-npm-7.0.0-0903313151-60142c7ddd.zip new file mode 100644 index 0000000..3da1d62 Binary files /dev/null and b/.yarn/cache/@types-webidl-conversions-npm-7.0.0-0903313151-60142c7ddd.zip differ diff --git a/.yarn/cache/@types-whatwg-url-npm-8.2.2-54c5c24e6c-5dc5afe078.zip b/.yarn/cache/@types-whatwg-url-npm-8.2.2-54c5c24e6c-5dc5afe078.zip new file mode 100644 index 0000000..cc6d5fd Binary files /dev/null and b/.yarn/cache/@types-whatwg-url-npm-8.2.2-54c5c24e6c-5dc5afe078.zip differ diff --git a/.yarn/cache/async-mutex-npm-0.3.2-600f6c46a1-620b771dfd.zip b/.yarn/cache/async-mutex-npm-0.3.2-600f6c46a1-620b771dfd.zip new file mode 100644 index 0000000..d9be006 Binary files /dev/null and b/.yarn/cache/async-mutex-npm-0.3.2-600f6c46a1-620b771dfd.zip differ diff --git a/.yarn/cache/axios-npm-1.4.0-4d7ce8ca3e-7fb6a4313b.zip b/.yarn/cache/axios-npm-1.4.0-4d7ce8ca3e-7fb6a4313b.zip new file mode 100644 index 0000000..9adaabc Binary files /dev/null and b/.yarn/cache/axios-npm-1.4.0-4d7ce8ca3e-7fb6a4313b.zip differ diff --git a/.yarn/cache/bcryptjs-npm-2.4.3-32de4957eb-0e80ed852a.zip b/.yarn/cache/bcryptjs-npm-2.4.3-32de4957eb-0e80ed852a.zip new file mode 100644 index 0000000..77c0245 Binary files /dev/null and b/.yarn/cache/bcryptjs-npm-2.4.3-32de4957eb-0e80ed852a.zip differ diff --git a/.yarn/cache/bowser-npm-2.11.0-33664d9063-29c3f01f22.zip b/.yarn/cache/bowser-npm-2.11.0-33664d9063-29c3f01f22.zip new file mode 100644 index 0000000..7860319 Binary files /dev/null and b/.yarn/cache/bowser-npm-2.11.0-33664d9063-29c3f01f22.zip differ diff --git a/.yarn/cache/bson-npm-4.7.2-77a08a4d01-f357d12c56.zip b/.yarn/cache/bson-npm-4.7.2-77a08a4d01-f357d12c56.zip new file mode 100644 index 0000000..1bf7858 Binary files /dev/null and b/.yarn/cache/bson-npm-4.7.2-77a08a4d01-f357d12c56.zip differ diff --git a/.yarn/cache/bson-npm-5.4.0-2f854c8216-1c07e3d09f.zip b/.yarn/cache/bson-npm-5.4.0-2f854c8216-1c07e3d09f.zip new file mode 100644 index 0000000..37035db Binary files /dev/null and b/.yarn/cache/bson-npm-5.4.0-2f854c8216-1c07e3d09f.zip differ diff --git a/.yarn/cache/buffer-crc32-npm-0.2.13-c4b6fceac1-06252347ae.zip b/.yarn/cache/buffer-crc32-npm-0.2.13-c4b6fceac1-06252347ae.zip new file mode 100644 index 0000000..96da9d8 Binary files /dev/null and b/.yarn/cache/buffer-crc32-npm-0.2.13-c4b6fceac1-06252347ae.zip differ diff --git a/.yarn/cache/buffer-equal-constant-time-npm-1.0.1-41826f3419-80bb945f5d.zip b/.yarn/cache/buffer-equal-constant-time-npm-1.0.1-41826f3419-80bb945f5d.zip new file mode 100644 index 0000000..b1f7def Binary files /dev/null and b/.yarn/cache/buffer-equal-constant-time-npm-1.0.1-41826f3419-80bb945f5d.zip differ diff --git a/.yarn/cache/class-transformer-npm-0.5.1-96b5161e6c-f191c8b4cc.zip b/.yarn/cache/class-transformer-npm-0.5.1-96b5161e6c-f191c8b4cc.zip new file mode 100644 index 0000000..0dadc13 Binary files /dev/null and b/.yarn/cache/class-transformer-npm-0.5.1-96b5161e6c-f191c8b4cc.zip differ diff --git a/.yarn/cache/class-validator-npm-0.14.0-b600d5fe72-f62e4a0ad2.zip b/.yarn/cache/class-validator-npm-0.14.0-b600d5fe72-f62e4a0ad2.zip new file mode 100644 index 0000000..7521af9 Binary files /dev/null and b/.yarn/cache/class-validator-npm-0.14.0-b600d5fe72-f62e4a0ad2.zip differ diff --git a/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip b/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip new file mode 100644 index 0000000..b2b0817 Binary files /dev/null and b/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip differ diff --git a/.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip b/.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip new file mode 100644 index 0000000..67c675e Binary files /dev/null and b/.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip differ diff --git a/.yarn/cache/cookie-parser-npm-1.4.6-a68f84d02a-1e5a63aa82.zip b/.yarn/cache/cookie-parser-npm-1.4.6-a68f84d02a-1e5a63aa82.zip new file mode 100644 index 0000000..6feae6f Binary files /dev/null and b/.yarn/cache/cookie-parser-npm-1.4.6-a68f84d02a-1e5a63aa82.zip differ diff --git a/.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip b/.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip new file mode 100644 index 0000000..e392e0d Binary files /dev/null and b/.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip differ diff --git a/.yarn/cache/dotenv-expand-npm-10.0.0-fa5b032ad9-2a38b470ef.zip b/.yarn/cache/dotenv-expand-npm-10.0.0-fa5b032ad9-2a38b470ef.zip new file mode 100644 index 0000000..5d6c73b Binary files /dev/null and b/.yarn/cache/dotenv-expand-npm-10.0.0-fa5b032ad9-2a38b470ef.zip differ diff --git a/.yarn/cache/dotenv-npm-16.1.4-c4499a21eb-c1b2e13df4.zip b/.yarn/cache/dotenv-npm-16.1.4-c4499a21eb-c1b2e13df4.zip new file mode 100644 index 0000000..755fcf3 Binary files /dev/null and b/.yarn/cache/dotenv-npm-16.1.4-c4499a21eb-c1b2e13df4.zip differ diff --git a/.yarn/cache/ecdsa-sig-formatter-npm-1.0.11-b6784e7852-207f9ab1c2.zip b/.yarn/cache/ecdsa-sig-formatter-npm-1.0.11-b6784e7852-207f9ab1c2.zip new file mode 100644 index 0000000..20b562d Binary files /dev/null and b/.yarn/cache/ecdsa-sig-formatter-npm-1.0.11-b6784e7852-207f9ab1c2.zip differ diff --git a/.yarn/cache/fast-xml-parser-npm-4.2.5-342a3689c5-d32b220055.zip b/.yarn/cache/fast-xml-parser-npm-4.2.5-342a3689c5-d32b220055.zip new file mode 100644 index 0000000..b83440e Binary files /dev/null and b/.yarn/cache/fast-xml-parser-npm-4.2.5-342a3689c5-d32b220055.zip differ diff --git a/.yarn/cache/fd-slicer-npm-1.1.0-3cade0050a-c8585fd571.zip b/.yarn/cache/fd-slicer-npm-1.1.0-3cade0050a-c8585fd571.zip new file mode 100644 index 0000000..1315962 Binary files /dev/null and b/.yarn/cache/fd-slicer-npm-1.1.0-3cade0050a-c8585fd571.zip differ diff --git a/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip new file mode 100644 index 0000000..bb911f5 Binary files /dev/null and b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip differ diff --git a/.yarn/cache/follow-redirects-npm-1.15.2-1ec1dd82be-faa66059b6.zip b/.yarn/cache/follow-redirects-npm-1.15.2-1ec1dd82be-faa66059b6.zip new file mode 100644 index 0000000..b50d775 Binary files /dev/null and b/.yarn/cache/follow-redirects-npm-1.15.2-1ec1dd82be-faa66059b6.zip differ diff --git a/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip b/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip new file mode 100644 index 0000000..91f5b6f Binary files /dev/null and b/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip differ diff --git a/.yarn/cache/get-port-npm-5.1.1-2f6074007a-0162663ffe.zip b/.yarn/cache/get-port-npm-5.1.1-2f6074007a-0162663ffe.zip new file mode 100644 index 0000000..bfec402 Binary files /dev/null and b/.yarn/cache/get-port-npm-5.1.1-2f6074007a-0162663ffe.zip differ diff --git a/.yarn/cache/jsonwebtoken-npm-9.0.0-36fd1594c0-b9181cecf9.zip b/.yarn/cache/jsonwebtoken-npm-9.0.0-36fd1594c0-b9181cecf9.zip new file mode 100644 index 0000000..0654b0b Binary files /dev/null and b/.yarn/cache/jsonwebtoken-npm-9.0.0-36fd1594c0-b9181cecf9.zip differ diff --git a/.yarn/cache/jsonwebtoken-npm-9.0.1-30d1a69741-0eafe26889.zip b/.yarn/cache/jsonwebtoken-npm-9.0.1-30d1a69741-0eafe26889.zip new file mode 100644 index 0000000..6233a0f Binary files /dev/null and b/.yarn/cache/jsonwebtoken-npm-9.0.1-30d1a69741-0eafe26889.zip differ diff --git a/.yarn/cache/jwa-npm-1.4.1-4f19d6572c-ff30ea7c2d.zip b/.yarn/cache/jwa-npm-1.4.1-4f19d6572c-ff30ea7c2d.zip new file mode 100644 index 0000000..e50529e Binary files /dev/null and b/.yarn/cache/jwa-npm-1.4.1-4f19d6572c-ff30ea7c2d.zip differ diff --git a/.yarn/cache/jws-npm-3.2.2-c1ae59c7af-f0213fe5b7.zip b/.yarn/cache/jws-npm-3.2.2-c1ae59c7af-f0213fe5b7.zip new file mode 100644 index 0000000..20d7e1e Binary files /dev/null and b/.yarn/cache/jws-npm-3.2.2-c1ae59c7af-f0213fe5b7.zip differ diff --git a/.yarn/cache/kareem-npm-2.5.1-79134fb43e-b019a960a7.zip b/.yarn/cache/kareem-npm-2.5.1-79134fb43e-b019a960a7.zip new file mode 100644 index 0000000..3ca67b7 Binary files /dev/null and b/.yarn/cache/kareem-npm-2.5.1-79134fb43e-b019a960a7.zip differ diff --git a/.yarn/cache/libphonenumber-js-npm-1.10.37-79b0943385-c28f762322.zip b/.yarn/cache/libphonenumber-js-npm-1.10.37-79b0943385-c28f762322.zip new file mode 100644 index 0000000..e73c758 Binary files /dev/null and b/.yarn/cache/libphonenumber-js-npm-1.10.37-79b0943385-c28f762322.zip differ diff --git a/.yarn/cache/md5-file-npm-5.0.0-e5f59abc62-c606a00ff5.zip b/.yarn/cache/md5-file-npm-5.0.0-e5f59abc62-c606a00ff5.zip new file mode 100644 index 0000000..3043a3c Binary files /dev/null and b/.yarn/cache/md5-file-npm-5.0.0-e5f59abc62-c606a00ff5.zip differ diff --git a/.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip b/.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip new file mode 100644 index 0000000..2bebede Binary files /dev/null and b/.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip differ diff --git a/.yarn/cache/mongodb-connection-string-url-npm-2.6.0-af011ba17f-1d662f0ecf.zip b/.yarn/cache/mongodb-connection-string-url-npm-2.6.0-af011ba17f-1d662f0ecf.zip new file mode 100644 index 0000000..6f9e3d5 Binary files /dev/null and b/.yarn/cache/mongodb-connection-string-url-npm-2.6.0-af011ba17f-1d662f0ecf.zip differ diff --git a/.yarn/cache/mongodb-memory-server-core-npm-8.13.0-c46ab8f8fc-05ee432d70.zip b/.yarn/cache/mongodb-memory-server-core-npm-8.13.0-c46ab8f8fc-05ee432d70.zip new file mode 100644 index 0000000..2be5583 Binary files /dev/null and b/.yarn/cache/mongodb-memory-server-core-npm-8.13.0-c46ab8f8fc-05ee432d70.zip differ diff --git a/.yarn/cache/mongodb-memory-server-npm-8.13.0-8ab039ac60-93bc1f40db.zip b/.yarn/cache/mongodb-memory-server-npm-8.13.0-8ab039ac60-93bc1f40db.zip new file mode 100644 index 0000000..5fbeeb5 Binary files /dev/null and b/.yarn/cache/mongodb-memory-server-npm-8.13.0-8ab039ac60-93bc1f40db.zip differ diff --git a/.yarn/cache/mongodb-npm-4.16.0-f6d128b4ab-f0b1347739.zip b/.yarn/cache/mongodb-npm-4.16.0-f6d128b4ab-f0b1347739.zip new file mode 100644 index 0000000..a76ec0b Binary files /dev/null and b/.yarn/cache/mongodb-npm-4.16.0-f6d128b4ab-f0b1347739.zip differ diff --git a/.yarn/cache/mongodb-npm-5.6.0-d7404fc735-5b1594b247.zip b/.yarn/cache/mongodb-npm-5.6.0-d7404fc735-5b1594b247.zip new file mode 100644 index 0000000..a5d7819 Binary files /dev/null and b/.yarn/cache/mongodb-npm-5.6.0-d7404fc735-5b1594b247.zip differ diff --git a/.yarn/cache/mongoose-npm-7.3.4-c1408e44ec-1ffdb8449c.zip b/.yarn/cache/mongoose-npm-7.3.4-c1408e44ec-1ffdb8449c.zip new file mode 100644 index 0000000..8070e06 Binary files /dev/null and b/.yarn/cache/mongoose-npm-7.3.4-c1408e44ec-1ffdb8449c.zip differ diff --git a/.yarn/cache/mpath-npm-0.9.0-e79cc94aea-1052f1f926.zip b/.yarn/cache/mpath-npm-0.9.0-e79cc94aea-1052f1f926.zip new file mode 100644 index 0000000..573b68f Binary files /dev/null and b/.yarn/cache/mpath-npm-0.9.0-e79cc94aea-1052f1f926.zip differ diff --git a/.yarn/cache/mquery-npm-5.0.0-e714f098ee-0617ead71e.zip b/.yarn/cache/mquery-npm-5.0.0-e714f098ee-0617ead71e.zip new file mode 100644 index 0000000..7a948d1 Binary files /dev/null and b/.yarn/cache/mquery-npm-5.0.0-e714f098ee-0617ead71e.zip differ diff --git a/.yarn/cache/multer-npm-1.4.5-lts.1-2b83a2d180-d6dfa78a6e.zip b/.yarn/cache/multer-npm-1.4.5-lts.1-2b83a2d180-d6dfa78a6e.zip new file mode 100644 index 0000000..af87d2f Binary files /dev/null and b/.yarn/cache/multer-npm-1.4.5-lts.1-2b83a2d180-d6dfa78a6e.zip differ diff --git a/.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip b/.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip new file mode 100644 index 0000000..1bb60ab Binary files /dev/null and b/.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip differ diff --git a/.yarn/cache/passport-jwt-npm-4.0.1-2841eea09e-0669d5bf8f.zip b/.yarn/cache/passport-jwt-npm-4.0.1-2841eea09e-0669d5bf8f.zip new file mode 100644 index 0000000..55765a9 Binary files /dev/null and b/.yarn/cache/passport-jwt-npm-4.0.1-2841eea09e-0669d5bf8f.zip differ diff --git a/.yarn/cache/passport-local-npm-1.0.0-ed89961a0c-86dc08b12f.zip b/.yarn/cache/passport-local-npm-1.0.0-ed89961a0c-86dc08b12f.zip new file mode 100644 index 0000000..14860f2 Binary files /dev/null and b/.yarn/cache/passport-local-npm-1.0.0-ed89961a0c-86dc08b12f.zip differ diff --git a/.yarn/cache/passport-npm-0.6.0-0a57682d5b-ef932ad671.zip b/.yarn/cache/passport-npm-0.6.0-0a57682d5b-ef932ad671.zip new file mode 100644 index 0000000..bf83650 Binary files /dev/null and b/.yarn/cache/passport-npm-0.6.0-0a57682d5b-ef932ad671.zip differ diff --git a/.yarn/cache/passport-strategy-npm-1.0.0-5648a82a66-5086693f25.zip b/.yarn/cache/passport-strategy-npm-1.0.0-5648a82a66-5086693f25.zip new file mode 100644 index 0000000..2a616e1 Binary files /dev/null and b/.yarn/cache/passport-strategy-npm-1.0.0-5648a82a66-5086693f25.zip differ diff --git a/.yarn/cache/pause-npm-0.0.1-0c421a299d-e96ee581b6.zip b/.yarn/cache/pause-npm-0.0.1-0c421a299d-e96ee581b6.zip new file mode 100644 index 0000000..a49838a Binary files /dev/null and b/.yarn/cache/pause-npm-0.0.1-0c421a299d-e96ee581b6.zip differ diff --git a/.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip b/.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip new file mode 100644 index 0000000..03b6b6d Binary files /dev/null and b/.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip differ diff --git a/.yarn/cache/proxy-from-env-npm-1.1.0-c13d07f26b-ed7fcc2ba0.zip b/.yarn/cache/proxy-from-env-npm-1.1.0-c13d07f26b-ed7fcc2ba0.zip new file mode 100644 index 0000000..a58e6bf Binary files /dev/null and b/.yarn/cache/proxy-from-env-npm-1.1.0-c13d07f26b-ed7fcc2ba0.zip differ diff --git a/.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip b/.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip new file mode 100644 index 0000000..218c565 Binary files /dev/null and b/.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip differ diff --git a/.yarn/cache/sift-npm-16.0.1-ad548f4923-5fe18a517a.zip b/.yarn/cache/sift-npm-16.0.1-ad548f4923-5fe18a517a.zip new file mode 100644 index 0000000..02309a0 Binary files /dev/null and b/.yarn/cache/sift-npm-16.0.1-ad548f4923-5fe18a517a.zip differ diff --git a/.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip b/.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip new file mode 100644 index 0000000..7c43c8b Binary files /dev/null and b/.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip differ diff --git a/.yarn/cache/strnum-npm-1.0.5-9ba11d2a0a-651b2031db.zip b/.yarn/cache/strnum-npm-1.0.5-9ba11d2a0a-651b2031db.zip new file mode 100644 index 0000000..46bb25a Binary files /dev/null and b/.yarn/cache/strnum-npm-1.0.5-9ba11d2a0a-651b2031db.zip differ diff --git a/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip b/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip new file mode 100644 index 0000000..6d7267b Binary files /dev/null and b/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip differ diff --git a/.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-44c3cc6767.zip b/.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-44c3cc6767.zip new file mode 100644 index 0000000..e272ccb Binary files /dev/null and b/.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-44c3cc6767.zip differ diff --git a/.yarn/cache/uuid-npm-8.3.2-eca0baba53-5575a8a75c.zip b/.yarn/cache/uuid-npm-8.3.2-eca0baba53-5575a8a75c.zip new file mode 100644 index 0000000..9b58328 Binary files /dev/null and b/.yarn/cache/uuid-npm-8.3.2-eca0baba53-5575a8a75c.zip differ diff --git a/.yarn/cache/uuid-npm-9.0.0-46c41e3e43-8dd2c83c43.zip b/.yarn/cache/uuid-npm-9.0.0-46c41e3e43-8dd2c83c43.zip new file mode 100644 index 0000000..90b6b78 Binary files /dev/null and b/.yarn/cache/uuid-npm-9.0.0-46c41e3e43-8dd2c83c43.zip differ diff --git a/.yarn/cache/validator-npm-13.9.0-54b07e9e81-e2c936f041.zip b/.yarn/cache/validator-npm-13.9.0-54b07e9e81-e2c936f041.zip new file mode 100644 index 0000000..d142823 Binary files /dev/null and b/.yarn/cache/validator-npm-13.9.0-54b07e9e81-e2c936f041.zip differ diff --git a/.yarn/cache/webidl-conversions-npm-7.0.0-e8c8e30c68-f05588567a.zip b/.yarn/cache/webidl-conversions-npm-7.0.0-e8c8e30c68-f05588567a.zip new file mode 100644 index 0000000..a2753a8 Binary files /dev/null and b/.yarn/cache/webidl-conversions-npm-7.0.0-e8c8e30c68-f05588567a.zip differ diff --git a/.yarn/cache/whatwg-url-npm-11.0.0-073529d93a-ed4826aaa5.zip b/.yarn/cache/whatwg-url-npm-11.0.0-073529d93a-ed4826aaa5.zip new file mode 100644 index 0000000..8da3cb7 Binary files /dev/null and b/.yarn/cache/whatwg-url-npm-11.0.0-073529d93a-ed4826aaa5.zip differ diff --git a/.yarn/cache/yauzl-npm-2.10.0-72e70ea021-7f21fe0bba.zip b/.yarn/cache/yauzl-npm-2.10.0-72e70ea021-7f21fe0bba.zip new file mode 100644 index 0000000..7a5f10c Binary files /dev/null and b/.yarn/cache/yauzl-npm-2.10.0-72e70ea021-7f21fe0bba.zip differ diff --git a/.yarnrc.yml b/.yarnrc.yml index 90940ef..eb62224 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,14 @@ yarnPath: .yarn/releases/yarn-3.6.1.cjs + +packageExtensions: + ts-loader@*: + dependencies: + webpack: "*" + + "@nestjs/passport@*": + dependencies: + passport: "*" + + "@nestjs/axios@*": + dependencies: + axios: "*" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..be4499c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +FROM node:16-alpine as base + +FROM base as builder + +WORKDIR /app + +COPY package.json . +COPY yarn.lock . +COPY .yarnrc.yml . +COPY .pnp.cjs . +COPY .pnp.loader.mjs . +COPY .yarn ./.yarn + +RUN yarn install + +COPY . . + +RUN yarn build + +FROM base AS runner + +WORKDIR /app + +COPY --from=builder /app/.yarn/releases ./.yarn/releases +COPY --from=builder /app/.yarn/cache ./.yarn/cache +COPY --from=builder /app/.yarn/unplugged ./.yarn/unplugged +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/.env ./.env + +COPY --from=builder /app/.pnp.cjs ./.pnp.cjs +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/yarn.lock ./yarn.lock +COPY --from=builder /app/.yarnrc.yml ./.yarnrc.yml + +ENV NODE_ENV production +ENV PORT 4000 +ENV HOSTNAME 0.0.0.0 + +EXPOSE 4000 + +CMD ["node", "-r", "./.pnp.cjs", "dist/main.js"] \ No newline at end of file diff --git a/package.json b/package.json index a0e50d9..4815b44 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "main": "index.js", "repository": "https://github.com/seoko97/SEOKO-server.git", - "author": "seokho10007 ", + "author": "seoko97 ", "license": "MIT", "scripts": { "build": "nest build", @@ -12,17 +12,33 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"", + "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest --runInBand", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@nestjs/axios": "^3.0.0", "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.0.0", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.1.0", + "@nestjs/mapped-types": "^2.0.2", + "@nestjs/mongoose": "^10.0.0", + "@nestjs/passport": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "bcryptjs": "^2.4.3", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "cookie-parser": "^1.4.6", + "crypto-js": "^4.1.1", + "mongoose": "^7.3.4", + "multer": "^1.4.5-lts.1", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, @@ -30,8 +46,10 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", + "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", + "@types/multer": "^1.4.7", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^5.59.11", @@ -42,6 +60,7 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-prettier": "^4.2.1", "jest": "^29.5.0", + "mongodb-memory-server": "^8.13.0", "prettier": "^2.8.8", "source-map-support": "^0.5.21", "supertest": "^6.3.3", @@ -57,16 +76,20 @@ "json", "ts" ], - "rootDir": "src", + "rootDir": ".", "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": [ - "**/*.(t|j)s" + "src/**/*.(t|j)s" ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" + "coverageDirectory": "./coverage", + "testEnvironment": "node", + "moduleNameMapper": { + "^@/(.*)$": "/src/$1", + "^test/(.*)$": "/test/$1" + } }, "packageManager": "yarn@3.6.1" } diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts deleted file mode 100644 index c260b57..0000000 --- a/src/app.controller.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; - -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; - -describe("AppController", () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe("root", () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe("Hello World!"); - }); - }); -}); diff --git a/src/app.controller.ts b/src/app.controller.ts deleted file mode 100644 index a7fc9f4..0000000 --- a/src/app.controller.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Controller, Get } from "@nestjs/common"; - -import { AppService } from "./app.service"; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} diff --git a/src/app.module.ts b/src/app.module.ts index 972b4b2..57ed619 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,11 +1,33 @@ import { Module } from "@nestjs/common"; +import { APP_GUARD } from "@nestjs/core"; -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; +import { AccessJwtAuthGuard } from "@/common/guards"; +import { CommonModule } from "@/common/modules"; +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { AuthModule } from "@/routes/auth/auth.module"; +import { ExperienceModule } from "@/routes/experience/experience.module"; +import { ImageModule } from "@/routes/image/image.module"; +import { PostModule } from "@/routes/post/post.module"; +import { ProjectModule } from "@/routes/project/project.module"; +import { SeriesModule } from "@/routes/series/series.module"; +import { SkillModule } from "@/routes/skill/skill.module"; +import { TagModule } from "@/routes/tag/tag.module"; +import { UserModule } from "@/routes/user/user.module"; @Module({ - imports: [], - controllers: [AppController], - providers: [AppService], + imports: [ + CommonModule, + SequenceModule, + UserModule, + AuthModule, + PostModule, + SeriesModule, + TagModule, + ProjectModule, + ExperienceModule, + SkillModule, + ImageModule, + ], + providers: [{ provide: APP_GUARD, useClass: AccessJwtAuthGuard }], }) export class AppModule {} diff --git a/src/app.service.ts b/src/app.service.ts deleted file mode 100644 index b00a667..0000000 --- a/src/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from "@nestjs/common"; - -@Injectable() -export class AppService { - getHello(): string { - return "Hello World!"; - } -} diff --git a/src/common/decorators/RealIp.decorator.ts b/src/common/decorators/RealIp.decorator.ts new file mode 100644 index 0000000..1be95a9 --- /dev/null +++ b/src/common/decorators/RealIp.decorator.ts @@ -0,0 +1,10 @@ +import { createParamDecorator } from "@nestjs/common"; + +const RealIp = createParamDecorator((data, ctx) => { + const req = ctx.switchToHttp().getRequest(); + const ip = req.headers["x-forwarded-for"] || req.headers["x-real-ip"] || "0.0.0.0"; + + return ip; +}); + +export { RealIp }; diff --git a/src/common/decorators/index.ts b/src/common/decorators/index.ts new file mode 100644 index 0000000..d7580e9 --- /dev/null +++ b/src/common/decorators/index.ts @@ -0,0 +1,2 @@ +export * from "./skip-auth.decorator"; +export * from "./user.decorator"; diff --git a/src/common/decorators/is-optional.decorator.ts b/src/common/decorators/is-optional.decorator.ts new file mode 100644 index 0000000..ee8b58c --- /dev/null +++ b/src/common/decorators/is-optional.decorator.ts @@ -0,0 +1,9 @@ +import { applyDecorators } from "@nestjs/common"; + +import { IsNotEmpty, IsOptional } from "class-validator"; + +const IsOptionalCustom = (..._decorators: PropertyDecorator[]) => { + return applyDecorators(IsOptional(), IsNotEmpty(), ..._decorators); +}; + +export { IsOptionalCustom }; diff --git a/src/common/decorators/skip-auth.decorator.ts b/src/common/decorators/skip-auth.decorator.ts new file mode 100644 index 0000000..a5c971c --- /dev/null +++ b/src/common/decorators/skip-auth.decorator.ts @@ -0,0 +1,7 @@ +import { SetMetadata } from "@nestjs/common"; + +import { IS_PUBLIC_KEY } from "@/utils/constants"; + +const Public = () => SetMetadata(IS_PUBLIC_KEY, true); + +export { Public }; diff --git a/src/common/decorators/transaction.decorator.ts b/src/common/decorators/transaction.decorator.ts new file mode 100644 index 0000000..82a3002 --- /dev/null +++ b/src/common/decorators/transaction.decorator.ts @@ -0,0 +1,44 @@ +import { InternalServerErrorException } from "@nestjs/common"; + +import { mongo } from "mongoose"; + +import { CONNECTION_NOT_FOUND, TRANSACTION_SESSION } from "@/common/transaction/constants"; +import { ALS } from "@/common/transaction/core/AsyncLocalStorage"; +import { ConnectionStore } from "@/common/transaction/core/ConnectionStore"; + +function Transactional(): MethodDecorator { + return (target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => { + const originalMethod = descriptor.value; + const als = new ALS(); + + descriptor.value = async function (...args: any[]) { + return als.run(async () => { + const connection = new ConnectionStore().getConnection(); + + if (!connection) { + throw new InternalServerErrorException(CONNECTION_NOT_FOUND); + } + + const session = await connection.startSession(); + als.set(TRANSACTION_SESSION, session); + session.startTransaction(); + + try { + const result = await originalMethod.apply(this, args); + await session.commitTransaction(); + return result; + } catch (e) { + if (!(e instanceof mongo.MongoServerError)) { + await session.abortTransaction(); + } + throw e; + } finally { + session.endSession(); + } + }); + }; + return descriptor; + }; +} + +export { Transactional }; diff --git a/src/common/decorators/user.decorator.ts b/src/common/decorators/user.decorator.ts new file mode 100644 index 0000000..12d9594 --- /dev/null +++ b/src/common/decorators/user.decorator.ts @@ -0,0 +1,11 @@ +import { createParamDecorator, ExecutionContext, UnauthorizedException } from "@nestjs/common"; + +export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + + if (!request.user) { + throw new UnauthorizedException("유저 정보가 존재하지 않습니다."); + } + + return request.user; +}); diff --git a/src/common/guards/index.ts b/src/common/guards/index.ts new file mode 100644 index 0000000..a55b4f2 --- /dev/null +++ b/src/common/guards/index.ts @@ -0,0 +1,2 @@ +export * from "./jwt-auth.guard"; +export * from "./local-auth.guard"; diff --git a/src/common/guards/jwt-auth.guard.ts b/src/common/guards/jwt-auth.guard.ts new file mode 100644 index 0000000..c977e49 --- /dev/null +++ b/src/common/guards/jwt-auth.guard.ts @@ -0,0 +1,79 @@ +import { ExecutionContext, Injectable, Type, UnauthorizedException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Reflector } from "@nestjs/core"; +import { AuthGuard, IAuthGuard } from "@nestjs/passport"; + +import { EJwtTokenType } from "@/types"; +import { AUTH_ERROR, IS_PUBLIC_KEY } from "@/utils/constants"; + +const generateJwtAuthGuard = (key: string): Type => { + @Injectable() + class JwtAuthGuard extends AuthGuard(`jwt-${key}`) { + HEADER: string; + + constructor(private readonly configService: ConfigService) { + super(); + + this.HEADER = this.configService.get( + key === EJwtTokenType.ACCESS ? "ACCESS_HEADER" : "REFRESH_HEADER", + ); + } + + handleRequest(err: unknown, user: any, info: any) { + if (err || !user) { + switch (info?.message) { + case "No auth token": + throw new UnauthorizedException(AUTH_ERROR.NO_SIGN); + case "jwt expired": + throw new UnauthorizedException(AUTH_ERROR.EXPIRED_TOKEN); + case "invalid token": + throw new UnauthorizedException(AUTH_ERROR.INVALID_TOKEN); + default: + throw err || new UnauthorizedException(info.message); + } + } + + return user; + } + + getRequest(context: ExecutionContext) { + const req = context.switchToHttp().getRequest(); + + const cookies = req.cookies; + + const token = cookies[this.HEADER]; + + if (token) req.headers.authorization = `Bearer ${token}`; + + return req; + } + } + + return JwtAuthGuard; +}; + +@Injectable() +export class AccessJwtAuthGuard extends generateJwtAuthGuard(EJwtTokenType.ACCESS) { + constructor( + private readonly reflector: Reflector, + private readonly configService: ConfigService, + ) { + super(configService); + } + + canActivate(context: ExecutionContext) { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + if (isPublic) { + return true; + } + + return super.canActivate(context); + } +} + +@Injectable() +export class RefreshJwtAuthGuard extends generateJwtAuthGuard(EJwtTokenType.REFRESH) {} diff --git a/src/common/guards/local-auth.guard.ts b/src/common/guards/local-auth.guard.ts new file mode 100644 index 0000000..e2e4c05 --- /dev/null +++ b/src/common/guards/local-auth.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from "@nestjs/common"; +import { AuthGuard } from "@nestjs/passport"; + +@Injectable() +export class LocalAuthGuard extends AuthGuard("local") {} diff --git a/src/common/modules/index.ts b/src/common/modules/index.ts new file mode 100644 index 0000000..06627d9 --- /dev/null +++ b/src/common/modules/index.ts @@ -0,0 +1,9 @@ +import { Module } from "@nestjs/common"; +import { ConfigModule } from "@nestjs/config"; + +import { MongoModule } from "@/common/modules/mongo.module"; + +@Module({ + imports: [ConfigModule.forRoot({ isGlobal: true }), MongoModule], +}) +export class CommonModule {} diff --git a/src/common/modules/mongo.module.ts b/src/common/modules/mongo.module.ts new file mode 100644 index 0000000..bb4c512 --- /dev/null +++ b/src/common/modules/mongo.module.ts @@ -0,0 +1,33 @@ +import { Module } from "@nestjs/common"; +import { ConfigModule, ConfigService } from "@nestjs/config"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { Connection } from "mongoose"; + +import { ConnectionStore } from "@/common/transaction/core/ConnectionStore"; +import { transactionPlugin } from "@/common/transaction/mongoosePluginCb"; + +@Module({ + imports: [ + MongooseModule.forRootAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + uri: configService.get("DB_URI"), + dbName: configService.get("DB_NAME"), + user: configService.get("DB_USERNAME"), + pass: configService.get("DB_PASSWORD"), + replicaSet: configService.get("DB_REPLICA_SET"), + + connectionFactory: (connection: Connection) => { + connection.plugin(transactionPlugin); + + new ConnectionStore().setConnection(connection); + + return connection; + }, + }), + inject: [ConfigService], + }), + ], +}) +export class MongoModule {} diff --git a/src/common/pipes/upload-image.pipe.ts b/src/common/pipes/upload-image.pipe.ts new file mode 100644 index 0000000..2b98465 --- /dev/null +++ b/src/common/pipes/upload-image.pipe.ts @@ -0,0 +1,17 @@ +import { BadRequestException, Injectable, PipeTransform } from "@nestjs/common"; + +import { TImageType } from "@/types"; +import { IMAGE_ERROR, IMAGE_UPLOAD_TYPES } from "@/utils/constants"; + +@Injectable() +export class ValidationImageParamPipe implements PipeTransform { + transform(value: TImageType) { + const index = IMAGE_UPLOAD_TYPES.indexOf(value); + + if (index === -1) { + throw new BadRequestException(IMAGE_ERROR.INVALID_UPLOAD_TYPE); + } + + return value; + } +} diff --git a/src/common/pipes/validate-objectid.pipe.ts b/src/common/pipes/validate-objectid.pipe.ts new file mode 100644 index 0000000..f427d70 --- /dev/null +++ b/src/common/pipes/validate-objectid.pipe.ts @@ -0,0 +1,17 @@ +import { Injectable, PipeTransform } from "@nestjs/common"; +import { BadRequestException } from "@nestjs/common"; + +import { Types } from "mongoose"; + +@Injectable() +export class ValidateObjectIdPipe implements PipeTransform { + async transform(value: string) { + const isValid = Types.ObjectId.isValid(value); + + if (!isValid) { + throw new BadRequestException("올바른 형식의 ID가 아닙니다."); + } + + return value; + } +} diff --git a/src/common/providers/auth-constant.provider.ts b/src/common/providers/auth-constant.provider.ts new file mode 100644 index 0000000..8dba775 --- /dev/null +++ b/src/common/providers/auth-constant.provider.ts @@ -0,0 +1,25 @@ +import { Inject, Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; + +@Injectable() +export class AuthConstantProvider { + JWT_SECRET_KEY: string; + ACCESS_HEADER: string; + REFRESH_HEADER: string; + ACCESS_EXPIRES: string; + REFRESH_EXPIRES: string; + COOKIE_MAX_AGE: string; + + constructor(@Inject(ConfigService) private readonly configService: ConfigService) { + this.init(); + } + + private init() { + this.JWT_SECRET_KEY = this.configService.get("JWT_SECRET_KEY"); + this.ACCESS_HEADER = this.configService.get("ACCESS_HEADER"); + this.REFRESH_HEADER = this.configService.get("REFRESH_HEADER"); + this.ACCESS_EXPIRES = this.configService.get("ACCESS_EXPIRES"); + this.REFRESH_EXPIRES = this.configService.get("REFRESH_EXPIRES"); + this.COOKIE_MAX_AGE = this.configService.get("COOKIE_MAX_AGE"); + } +} diff --git a/src/common/repository/base.repository.ts b/src/common/repository/base.repository.ts new file mode 100644 index 0000000..953e809 --- /dev/null +++ b/src/common/repository/base.repository.ts @@ -0,0 +1,62 @@ +import { FilterQuery, Document as MDocument, Model, ProjectionType, QueryOptions } from "mongoose"; + +import { SequenceRepository } from "@/common/sequence/sequence.repository"; + +export class BaseRepository { + constructor( + private readonly model: Model, + private readonly sequenceRepository: SequenceRepository, + ) {} + + async create(data: CreateDto): Promise { + const modelName = this.model.modelName.toLowerCase(); + + const nid = await this.sequenceRepository.getNextSequence(modelName); + + return this.model.create({ nid, ...data }); + } + + async update(_id: string, data: UpdateDto) { + return this.model.updateOne({ _id }, data); + } + + async findOneAndUpdate( + filter: FilterQuery, + data: UpdateDto, + options: QueryOptions = {}, + ) { + return this.model.findOneAndUpdate(filter, data, { new: true, ...options }); + } + + async findOneAndDelete(filter: FilterQuery, options: QueryOptions = {}) { + return this.model.findOneAndDelete(filter, options); + } + + async delete(_id: string) { + await this.model.deleteOne({ _id }); + } + + async getOne( + filter: FilterQuery = {}, + projection: ProjectionType = {}, + options: QueryOptions = {}, + ) { + return this.model.findOne(filter, projection, options); + } + + async getById( + _id: string, + projection: ProjectionType = {}, + options: QueryOptions = {}, + ) { + return this.model.findById(_id, projection, options); + } + + async getAll( + filter: FilterQuery = {}, + projection: ProjectionType = {}, + options: QueryOptions = {}, + ) { + return this.model.find(filter, projection, options); + } +} diff --git a/src/common/schema/base.schema.ts b/src/common/schema/base.schema.ts new file mode 100644 index 0000000..6d117dd --- /dev/null +++ b/src/common/schema/base.schema.ts @@ -0,0 +1,19 @@ +import { Schema, Prop } from "@nestjs/mongoose"; + +import { IsDateString, IsMongoId, IsNumber } from "class-validator"; + +@Schema({ _id: true }) +export class BaseSchema { + @IsMongoId() + _id!: string; + + @IsDateString() + createdAt!: Date; + + @IsDateString() + updatedAt!: Date; + + @IsNumber() + @Prop({ require: true, unique: true }) + nid!: number; +} diff --git a/src/common/sequence/sequence.module.ts b/src/common/sequence/sequence.module.ts new file mode 100644 index 0000000..69bec8a --- /dev/null +++ b/src/common/sequence/sequence.module.ts @@ -0,0 +1,12 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceRepository } from "./sequence.repository"; +import { Sequence, SequenceSchema } from "./sequence.schema"; + +@Module({ + imports: [MongooseModule.forFeature([{ name: Sequence.name, schema: SequenceSchema }])], + exports: [SequenceRepository], + providers: [SequenceRepository], +}) +export class SequenceModule {} diff --git a/src/common/sequence/sequence.repository.ts b/src/common/sequence/sequence.repository.ts new file mode 100644 index 0000000..c97eae6 --- /dev/null +++ b/src/common/sequence/sequence.repository.ts @@ -0,0 +1,25 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { Sequence, SequenceModel } from "@/common/sequence/sequence.schema"; + +@Injectable() +export class SequenceRepository { + constructor(@InjectModel(Sequence.name) protected readonly sequenceModel: SequenceModel) {} + + async getNextSequence(target: string) { + const sequence = await this.sequenceModel.findOne({ target }); + + if (sequence) { + sequence.seq += 1; + + await sequence.save(); + + return sequence.seq; + } else { + const sequenceDocument = await this.sequenceModel.create({ target }); + + return sequenceDocument.seq; + } + } +} diff --git a/src/common/sequence/sequence.schema.ts b/src/common/sequence/sequence.schema.ts new file mode 100644 index 0000000..9a5ac57 --- /dev/null +++ b/src/common/sequence/sequence.schema.ts @@ -0,0 +1,17 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { Document, Model } from "mongoose"; + +export type SequenceDocument = Sequence & Document; +export type SequenceModel = Model; + +@Schema() +export class Sequence { + @Prop({ required: true }) + target: string; + + @Prop({ required: true, default: 0 }) + seq: number; +} + +export const SequenceSchema = SchemaFactory.createForClass(Sequence); diff --git a/src/common/strategies/index.ts b/src/common/strategies/index.ts new file mode 100644 index 0000000..23bfe6e --- /dev/null +++ b/src/common/strategies/index.ts @@ -0,0 +1,2 @@ +export * from "./jwt.strategy"; +export * from "./local.strategy"; diff --git a/src/common/strategies/jwt.strategy.ts b/src/common/strategies/jwt.strategy.ts new file mode 100644 index 0000000..ee89025 --- /dev/null +++ b/src/common/strategies/jwt.strategy.ts @@ -0,0 +1,65 @@ +import { Request } from "express"; + +import { Injectable, UnauthorizedException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PassportStrategy } from "@nestjs/passport"; + +import { ExtractJwt, Strategy as JStrategy } from "passport-jwt"; + +import { UserService } from "@/routes/user/user.service"; +import { EJwtTokenType, TTokenUser } from "@/types"; +import { AUTH_ERROR } from "@/utils/constants"; + +@Injectable() +export class AccessJwtStrategy extends PassportStrategy(JStrategy, `jwt-${EJwtTokenType.ACCESS}`) { + constructor(private readonly configService: ConfigService) { + const JWT_SECRET = configService.get("JWT_SECRET_KEY"); + + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: JWT_SECRET, + }); + } + + validate(payload: TTokenUser) { + return { _id: payload._id }; + } +} + +@Injectable() +export class RefreshJwtStrategy extends PassportStrategy( + JStrategy, + `jwt-${EJwtTokenType.REFRESH}`, +) { + REFRESH_HEADER: string; + + constructor( + private readonly configService: ConfigService, + private readonly userService: UserService, + ) { + const JWT_SECRET = configService.get("JWT_SECRET_KEY"); + const REFRESH_HEADER = configService.get("REFRESH_HEADER"); + + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: JWT_SECRET, + passReqToCallback: true, + }); + + this.REFRESH_HEADER = REFRESH_HEADER; + } + + async validate(req: Request, payload: TTokenUser) { + const refreshToken = req.cookies?.[this.REFRESH_HEADER]; + + const user = await this.userService.getById(payload._id); + + if (!refreshToken || !user || user.refreshToken !== refreshToken) { + throw new UnauthorizedException(AUTH_ERROR.UNAUTHORIZED); + } + + return { _id: payload._id }; + } +} diff --git a/src/common/strategies/local.strategy.ts b/src/common/strategies/local.strategy.ts new file mode 100644 index 0000000..0fe8310 --- /dev/null +++ b/src/common/strategies/local.strategy.ts @@ -0,0 +1,30 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; +import { PassportStrategy } from "@nestjs/passport"; + +import * as bcrypt from "bcryptjs"; +import { Strategy as LStrategy } from "passport-local"; + +import { UserService } from "@/routes/user/user.service"; +import { USER_ERROR } from "@/utils/constants"; + +@Injectable() +export class LocalStrategy extends PassportStrategy(LStrategy) { + constructor(private readonly userService: UserService) { + super({ + usernameField: "userId", + passwordField: "password", + }); + } + + async validate(userId: string, password: string): Promise { + const _user = await this.userService.getByUserId(userId); + + if (!_user) throw new BadRequestException(USER_ERROR.NOT_FOUND); + + const isCompare = await bcrypt.compare(password, _user.password); + + if (!isCompare) throw new BadRequestException(USER_ERROR.PASSWORD_NOT_MATCH); + + return this.userService.getById(_user.id); + } +} diff --git a/src/common/transaction/constants.ts b/src/common/transaction/constants.ts new file mode 100644 index 0000000..441ff70 --- /dev/null +++ b/src/common/transaction/constants.ts @@ -0,0 +1,34 @@ +import { EMiddlewareTypes } from "@/common/transaction/type"; + +const TRANSACTION_KEY = "transaction"; +const TRANSACTION_SESSION = Symbol("transaction_session"); + +const documentAndQueryMiddleware = ["updateOne", "deleteOne"] as const; + +const middlewareGroups = { + [EMiddlewareTypes.document]: ["save", ...documentAndQueryMiddleware], + [EMiddlewareTypes.query]: [ + "deleteOne", + "deleteMany", + "updateMany", + "find", + "findOne", + "create", + "findById", + "findOneAndDelete", + "findOneAndUpdate", + "updateOne", + ], + [EMiddlewareTypes.aggregate]: ["aggregate"], + [EMiddlewareTypes.model]: ["insertMany"], +} as const; + +const CONNECTION_NOT_FOUND = "Connection이 존재하지 않습니다."; + +export { + TRANSACTION_KEY, + TRANSACTION_SESSION, + CONNECTION_NOT_FOUND, + documentAndQueryMiddleware, + middlewareGroups, +}; diff --git a/src/common/transaction/core/AsyncLocalStorage.ts b/src/common/transaction/core/AsyncLocalStorage.ts new file mode 100644 index 0000000..c685900 --- /dev/null +++ b/src/common/transaction/core/AsyncLocalStorage.ts @@ -0,0 +1,28 @@ +import { AsyncLocalStorage } from "async_hooks"; + +class ALS { + private static instance: ALS = new ALS(); + private asyncLocalStorage = new AsyncLocalStorage>(); + + constructor() { + return ALS.instance; + } + + get store() { + return this.asyncLocalStorage.getStore(); + } + + async run(fn: (...args: any[]) => any): Promise { + return await this.asyncLocalStorage.run(new Map(), fn); + } + + set(key: any, value: any) { + return this.store?.set(key, value); + } + + get(key: any): T { + return this.store?.get(key); + } +} + +export { ALS }; diff --git a/src/common/transaction/core/ConnectionStore.ts b/src/common/transaction/core/ConnectionStore.ts new file mode 100644 index 0000000..3b9caf3 --- /dev/null +++ b/src/common/transaction/core/ConnectionStore.ts @@ -0,0 +1,22 @@ +import { Connection } from "mongoose"; + +import { TRANSACTION_KEY } from "@/common/transaction/constants"; + +class ConnectionStore { + private static instance: ConnectionStore = new ConnectionStore(); + private _connections: { [K: string]: Connection } = {}; + + constructor() { + return ConnectionStore.instance; + } + + setConnection(connection: Connection, connectionName = TRANSACTION_KEY) { + this._connections[connectionName] = connection; + } + + getConnection(connectionName = TRANSACTION_KEY) { + return this._connections[connectionName]; + } +} + +export { ConnectionStore }; diff --git a/src/common/transaction/mongoosePluginCb.ts b/src/common/transaction/mongoosePluginCb.ts new file mode 100644 index 0000000..e966e62 --- /dev/null +++ b/src/common/transaction/mongoosePluginCb.ts @@ -0,0 +1,70 @@ +import { + Model, + Schema, + ClientSession, + MongooseQueryMiddleware, + MongooseDocumentMiddleware, + Document, + Query, + Aggregate, +} from "mongoose"; + +import { + TRANSACTION_SESSION, + documentAndQueryMiddleware, + middlewareGroups, +} from "@/common/transaction/constants"; +import { ALS } from "@/common/transaction/core/AsyncLocalStorage"; +import { EMiddlewareTypes, TMiddlewareType } from "@/common/transaction/type"; + +function transactionPlugin(schema: Schema) { + for (const middlewareType in middlewareGroups) { + middlewareGroups[middlewareType as TMiddlewareType].forEach((method) => { + if (middlewareType === EMiddlewareTypes.model) { + overwriteMethod(schema, method); + } else if ( + middlewareType === EMiddlewareTypes.document && + documentAndQueryMiddleware.includes(method) + ) { + schema.pre(method as MongooseDocumentMiddleware, { document: true, query: true }, preCb); + } else { + schema.pre(method as MongooseQueryMiddleware, preCb); + } + }); + } +} + +function preCb(this: any, next: any) { + const als = new ALS(); + const session = als.get(TRANSACTION_SESSION); + + if (this instanceof Document) { + this.$session() || this.$session(session); + } else if (this instanceof Query) { + this.getOptions().session || this.session(session); + } else if (this instanceof Aggregate) { + this.options.session || this.session(session); + } + + next(); +} + +function overwriteMethod(schema: Schema, method: string) { + const als = new ALS(); + + schema.statics[method] = function (...args: any) { + const session = als.get(TRANSACTION_SESSION); + + if (session) { + const options = args[1] || {}; + + if (!options?.session) { + args[1] = Object.assign(options, { session }); + } + } + + return Model[method].apply(this, args); + }; +} + +export { transactionPlugin }; diff --git a/src/common/transaction/type.ts b/src/common/transaction/type.ts new file mode 100644 index 0000000..8fae156 --- /dev/null +++ b/src/common/transaction/type.ts @@ -0,0 +1,10 @@ +const enum EMiddlewareTypes { + document = "document", + query = "query", + aggregate = "aggregate", + model = "model", +} + +type TMiddlewareType = keyof typeof EMiddlewareTypes; + +export { EMiddlewareTypes, TMiddlewareType }; diff --git a/src/main.ts b/src/main.ts index f523fae..67b8c49 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,36 @@ +import { ValidationPipe } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { NestFactory } from "@nestjs/core"; +import * as cookieParser from "cookie-parser"; + import { AppModule } from "@/app.module"; async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(4000); + + app.setGlobalPrefix("api"); + + const configService = app.get(ConfigService); + + const PORT = configService.get("PORT"); + const HOST = configService.get("HOST"); + + app.use(cookieParser()); + app.enableCors({ + origin: HOST, + credentials: true, + }); + + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: true, + transform: true, + }), + ); + + await app.listen(PORT); } + bootstrap(); diff --git a/src/routes/auth/auth.controller.ts b/src/routes/auth/auth.controller.ts new file mode 100644 index 0000000..3616578 --- /dev/null +++ b/src/routes/auth/auth.controller.ts @@ -0,0 +1,75 @@ +import { Response } from "express"; + +import { Controller, Post, Res, UseGuards } from "@nestjs/common"; + +import { Public, User } from "@/common/decorators"; +import { LocalAuthGuard, RefreshJwtAuthGuard } from "@/common/guards"; +import { AuthService } from "@/routes/auth/auth.service"; +import { SigninDTO } from "@/routes/auth/dto/signin.dto"; +import { UserService } from "@/routes/user/user.service"; +import { EJwtTokenType, TTokenUser } from "@/types"; + +@Controller("auth") +export class AuthController { + constructor(private userService: UserService, private authService: AuthService) {} + + // localGuard를 통해 검증된 User 정보를 가져옴 + // 해당 User 정보를 통해 access token과 refresh token을 발급 + // 발급된 token을 response header에 담아서 전송 + @Public() + @UseGuards(LocalAuthGuard) + @Post("signin") + async signin(_: SigninDTO, @User() _user: TTokenUser, @Res({ passthrough: true }) res: Response) { + const user = await this.userService.getById(_user._id); + + const [accessToken, refreshToken] = await this.authService.signin(user); + + await this.userService.updateRefreshToken(user._id, refreshToken); + + this.authService.registerTokenInCookie({ + type: EJwtTokenType.ACCESS, + token: accessToken, + res, + }); + + this.authService.registerTokenInCookie({ + type: EJwtTokenType.REFRESH, + token: refreshToken, + res, + }); + + return { username: user.username }; + } + + // AuthGuard를 통해 검증된 User 정보를 가져옴 + // response header에 담겨있는 token을 제거 + @Post("signout") + async signout(@User() _user: TTokenUser, @Res({ passthrough: true }) res: Response) { + await this.userService.updateRefreshToken(_user._id, null); + + this.authService.clearCookie(res); + + return true; + } + + // AuthGuard를 통해 검증된 User 정보를 가져옴 + // 검증된 User 정보를 통해 access token 재발급 + @Public() + @UseGuards(RefreshJwtAuthGuard) + @Post("refresh") + async refresh(@User() _user: TTokenUser, @Res({ passthrough: true }) res: Response) { + const user = await this.userService.getById(_user._id); + + await this.authService.verifyRefreshToken(user.refreshToken); + + const [accessToken] = await this.authService.signin(_user); + + this.authService.registerTokenInCookie({ + type: EJwtTokenType.ACCESS, + token: accessToken, + res, + }); + + return true; + } +} diff --git a/src/routes/auth/auth.module.ts b/src/routes/auth/auth.module.ts new file mode 100644 index 0000000..810d28a --- /dev/null +++ b/src/routes/auth/auth.module.ts @@ -0,0 +1,31 @@ +import { Module } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { JwtModule, JwtService } from "@nestjs/jwt"; + +import { AuthConstantProvider } from "@/common/providers/auth-constant.provider"; +import { AccessJwtStrategy, LocalStrategy, RefreshJwtStrategy } from "@/common/strategies"; +import { AuthController } from "@/routes/auth/auth.controller"; +import { AuthService } from "@/routes/auth/auth.service"; +import { UserModule } from "@/routes/user/user.module"; + +@Module({ + imports: [ + UserModule, + JwtModule.registerAsync({ + inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + secret: configService.get("JWT_SECRET_KEY"), + }), + }), + ], + controllers: [AuthController], + providers: [ + AuthService, + JwtService, + AuthConstantProvider, + LocalStrategy, + AccessJwtStrategy, + RefreshJwtStrategy, + ], +}) +export class AuthModule {} diff --git a/src/routes/auth/auth.service.ts b/src/routes/auth/auth.service.ts new file mode 100644 index 0000000..5c1a96f --- /dev/null +++ b/src/routes/auth/auth.service.ts @@ -0,0 +1,76 @@ +import { CookieOptions, Response } from "express"; + +import { Injectable, UnauthorizedException } from "@nestjs/common"; +import { JwtService, JwtSignOptions } from "@nestjs/jwt"; + +import { AuthConstantProvider } from "@/common/providers/auth-constant.provider"; +import { EJwtTokenType, IRegisterTokenInCookieArgs, TTokenUser } from "@/types"; +import { AUTH_ERROR } from "@/utils/constants"; + +@Injectable() +export class AuthService { + constructor( + private readonly jwtService: JwtService, + private readonly authConstantProvider: AuthConstantProvider, + ) {} + + // 로그인시 access token과 refresh token을 발급 + async signin(user: TTokenUser) { + const { ACCESS_EXPIRES, REFRESH_EXPIRES } = this.authConstantProvider; + + const accessOptions: JwtSignOptions = { expiresIn: ACCESS_EXPIRES }; + const refreshOptions: JwtSignOptions = { expiresIn: REFRESH_EXPIRES }; + + const accessToken = this.signature(user._id, accessOptions); + const refreshToken = this.signature(user._id, refreshOptions); + + return [accessToken, refreshToken]; + } + + // refresh token 유효성 검사 + // 서버에 저장된 유저 토큰이 유효한 토큰인지 확인 + async verifyRefreshToken(refreshToken: string) { + try { + const options = { secret: this.authConstantProvider.JWT_SECRET_KEY }; + + const isVerify = await this.jwtService.verify(refreshToken, options); + + if (!Boolean(isVerify)) throw new UnauthorizedException(); + + return true; + } catch (e) { + throw new UnauthorizedException(AUTH_ERROR.UNAUTHORIZED); + } + } + + // 입력받은 데이터를 통해 토큰을 발급 + signature(_id: string, options: JwtSignOptions) { + options.secret = this.authConstantProvider.JWT_SECRET_KEY; + + return this.jwtService.sign({ _id }, options); + } + + // response header에 access token과 refresh token을 등록 + registerTokenInCookie({ type, token, res }: IRegisterTokenInCookieArgs) { + const { ACCESS_HEADER, REFRESH_HEADER, COOKIE_MAX_AGE } = this.authConstantProvider; + + const isRefresh = type === EJwtTokenType.REFRESH; + const tokenName = isRefresh ? REFRESH_HEADER : ACCESS_HEADER; + + const cookieOptions: CookieOptions = { + httpOnly: isRefresh, + maxAge: Number(COOKIE_MAX_AGE), + secure: true, + }; + + res.cookie(tokenName, token, cookieOptions); + } + + // response header에서 access token과 refresh token을 삭제 + clearCookie(res: Response) { + const { ACCESS_HEADER, REFRESH_HEADER } = this.authConstantProvider; + + res.clearCookie(ACCESS_HEADER); + res.clearCookie(REFRESH_HEADER); + } +} diff --git a/src/routes/auth/dto/signin.dto.ts b/src/routes/auth/dto/signin.dto.ts new file mode 100644 index 0000000..5061b30 --- /dev/null +++ b/src/routes/auth/dto/signin.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { User } from "@/routes/user/user.schema"; + +export class SigninDTO extends PickType(User, ["userId", "password"]) {} diff --git a/src/routes/experience/dto/create-experience.dto.ts b/src/routes/experience/dto/create-experience.dto.ts new file mode 100644 index 0000000..22f44dd --- /dev/null +++ b/src/routes/experience/dto/create-experience.dto.ts @@ -0,0 +1,10 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { Experience } from "@/routes/experience/experience.schema"; + +export class CreateExperienceDto extends PickType(Experience, [ + "title", + "description", + "start", + "end", +]) {} diff --git a/src/routes/experience/dto/update-experience.dto.ts b/src/routes/experience/dto/update-experience.dto.ts new file mode 100644 index 0000000..527b3ab --- /dev/null +++ b/src/routes/experience/dto/update-experience.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from "@nestjs/mapped-types"; + +import { CreateExperienceDto } from "@/routes/experience/dto/create-experience.dto"; + +export class UpdateExperienceDto extends PartialType(CreateExperienceDto) {} diff --git a/src/routes/experience/experience.controller.ts b/src/routes/experience/experience.controller.ts new file mode 100644 index 0000000..68341f3 --- /dev/null +++ b/src/routes/experience/experience.controller.ts @@ -0,0 +1,35 @@ +import { Body, Controller, Delete, Get, Param, Post, Put } from "@nestjs/common"; + +import { Public } from "@/common/decorators"; +import { ValidateObjectIdPipe } from "@/common/pipes/validate-objectid.pipe"; +import { CreateExperienceDto } from "@/routes/experience/dto/create-experience.dto"; +import { UpdateExperienceDto } from "@/routes/experience/dto/update-experience.dto"; +import { ExperienceService } from "@/routes/experience/experience.service"; + +@Controller("experiences") +export class ExperienceController { + constructor(private readonly experienceService: ExperienceService) {} + + @Post() + async create(@Body() data: CreateExperienceDto) { + return this.experienceService.create(data); + } + + @Put(":_id") + async update(@Param("_id", ValidateObjectIdPipe) _id: string, @Body() data: UpdateExperienceDto) { + return this.experienceService.update(_id, { ...data }); + } + + @Delete(":_id") + async delete(@Param("_id", ValidateObjectIdPipe) _id: string) { + await this.experienceService.delete(_id); + + return true; + } + + @Public() + @Get() + async getAll() { + return this.experienceService.getAll(); + } +} diff --git a/src/routes/experience/experience.module.ts b/src/routes/experience/experience.module.ts new file mode 100644 index 0000000..0dcb153 --- /dev/null +++ b/src/routes/experience/experience.module.ts @@ -0,0 +1,20 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { ExperienceRepository } from "@/routes/experience/experience.repository"; +import { Experience, ExperienceSchema } from "@/routes/experience/experience.schema"; + +import { ExperienceController } from "./experience.controller"; +import { ExperienceService } from "./experience.service"; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: Experience.name, schema: ExperienceSchema }]), + SequenceModule, + ], + providers: [ExperienceService, ExperienceRepository], + controllers: [ExperienceController], + exports: [ExperienceService], +}) +export class ExperienceModule {} diff --git a/src/routes/experience/experience.repository.ts b/src/routes/experience/experience.repository.ts new file mode 100644 index 0000000..bdb8e48 --- /dev/null +++ b/src/routes/experience/experience.repository.ts @@ -0,0 +1,20 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { + Experience, + ExperienceDocument, + ExperienceModel, +} from "@/routes/experience/experience.schema"; + +@Injectable() +export class ExperienceRepository extends BaseRepository { + constructor( + @InjectModel(Experience.name) experienceModel: ExperienceModel, + sequenceRepository: SequenceRepository, + ) { + super(experienceModel, sequenceRepository); + } +} diff --git a/src/routes/experience/experience.schema.ts b/src/routes/experience/experience.schema.ts new file mode 100644 index 0000000..2d79e02 --- /dev/null +++ b/src/routes/experience/experience.schema.ts @@ -0,0 +1,31 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsDateString, IsString } from "class-validator"; +import { Document, Model } from "mongoose"; + +import { IsOptionalCustom } from "@/common/decorators/is-optional.decorator"; +import { BaseSchema } from "@/common/schema/base.schema"; + +export type ExperienceDocument = Experience & Document; +export type ExperienceModel = Model; + +@Schema({ timestamps: true }) +export class Experience extends BaseSchema { + @IsString() + @Prop({ required: true, unique: true }) + title!: string; + + @IsString() + @Prop({ required: true }) + description!: string; + + @IsDateString() + @Prop({ required: true }) + start!: string; + + @IsOptionalCustom(IsDateString()) + @Prop({ required: false, default: null }) + end?: string; +} + +export const ExperienceSchema = SchemaFactory.createForClass(Experience); diff --git a/src/routes/experience/experience.service.ts b/src/routes/experience/experience.service.ts new file mode 100644 index 0000000..a3a7240 --- /dev/null +++ b/src/routes/experience/experience.service.ts @@ -0,0 +1,58 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; + +import { FilterQuery } from "mongoose"; + +import { CreateExperienceDto } from "@/routes/experience/dto/create-experience.dto"; +import { UpdateExperienceDto } from "@/routes/experience/dto/update-experience.dto"; +import { ExperienceRepository } from "@/routes/experience/experience.repository"; +import { SkillDocument } from "@/routes/skill/skill.schema"; +import { EXPERIENCE_ERROR } from "@/utils/constants"; + +@Injectable() +export class ExperienceService { + constructor(private readonly experienceRepository: ExperienceRepository) {} + + async create(data: CreateExperienceDto) { + await this.checkToExist({ title: data.title }); + + return this.experienceRepository.create(data); + } + + async update(_id: string, data: UpdateExperienceDto) { + await this.checkToExistById(_id); + + await this.checkToExist({ title: data.title, _id: { $ne: _id } }); + + return this.experienceRepository.findOneAndUpdate({ _id }, data); + } + + async delete(_id: string) { + await this.checkToExistById(_id); + + return this.experienceRepository.delete(_id); + } + + async getAll() { + return this.experienceRepository.getAll({}, {}, { sort: { start: -1 } }); + } + + async checkToExistById(_id: string) { + const experience = await this.experienceRepository.getById(_id); + + if (!experience) { + throw new BadRequestException(EXPERIENCE_ERROR.NOT_FOUND); + } + + return true; + } + + async checkToExist(query: FilterQuery) { + const experience = await this.experienceRepository.getOne(query); + + if (experience) { + throw new BadRequestException(EXPERIENCE_ERROR.ALREADY_EXISTS); + } + + return true; + } +} diff --git a/src/routes/image/image.controller.ts b/src/routes/image/image.controller.ts new file mode 100644 index 0000000..7caf940 --- /dev/null +++ b/src/routes/image/image.controller.ts @@ -0,0 +1,20 @@ +import { Controller, Param, Post, UploadedFile, UseInterceptors } from "@nestjs/common"; +import { FileInterceptor } from "@nestjs/platform-express"; + +import { ValidationImageParamPipe } from "@/common/pipes/upload-image.pipe"; +import { ImageService } from "@/routes/image/image.service"; +import { TImageType } from "@/types"; + +@Controller("images") +export class ImageController { + constructor(private imageService: ImageService) {} + + @Post(":type") + @UseInterceptors(FileInterceptor("image")) + async upload( + @Param("type", ValidationImageParamPipe) type: TImageType, + @UploadedFile() file: Express.Multer.File, + ) { + return this.imageService.addImage(type, file); + } +} diff --git a/src/routes/image/image.module.ts b/src/routes/image/image.module.ts new file mode 100644 index 0000000..2eb2c74 --- /dev/null +++ b/src/routes/image/image.module.ts @@ -0,0 +1,22 @@ +import { HttpModule } from "@nestjs/axios"; +import { Module } from "@nestjs/common"; +import { MulterModule } from "@nestjs/platform-express"; + +import { ImageController } from "@/routes/image/image.controller"; +import { ImageService } from "@/routes/image/image.service"; +import { multerOptionsFactory } from "@/utils/multerOptionsFactory"; + +@Module({ + imports: [ + HttpModule.register({ + timeout: 5000, + maxRedirects: 5, + }), + MulterModule.registerAsync({ + useFactory: multerOptionsFactory, + }), + ], + controllers: [ImageController], + providers: [ImageService], +}) +export class ImageModule {} diff --git a/src/routes/image/image.service.ts b/src/routes/image/image.service.ts new file mode 100644 index 0000000..091fa2f --- /dev/null +++ b/src/routes/image/image.service.ts @@ -0,0 +1,51 @@ +import { HttpService, HttpModuleOptions } from "@nestjs/axios"; +import { BadRequestException, Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; + +import { firstValueFrom } from "rxjs"; + +import { TImageType } from "@/types"; + +@Injectable() +export class ImageService { + private readonly IMAGE_UPLOAD_URL: string; + private readonly IMAGE_APP_KEY: string; + private readonly IMAGE_OPTION: HttpModuleOptions; + + constructor(private readonly httpService: HttpService, configService: ConfigService) { + this.IMAGE_UPLOAD_URL = configService.get("IMAGE_UPLOAD_URL"); + this.IMAGE_APP_KEY = configService.get("IMAGE_APP_KEY"); + this.IMAGE_OPTION = { + headers: { + Authorization: `${configService.get("IMAGE_UPLOAD_SECRET_KEY")}`, + "Content-Type": "application/octet-stream", + }, + }; + } + + async addImage(type: TImageType, image: Express.Multer.File) { + try { + const { originalname, buffer } = image; + + const filename = decodeURI(originalname).replaceAll(" ", "_"); + + const imageRes = await firstValueFrom( + this.httpService.put( + `${this.IMAGE_UPLOAD_URL}/appkeys/${this.IMAGE_APP_KEY}/images?path=/${type}/${filename}&overwrite=true`, + buffer, + this.IMAGE_OPTION, + ), + ); + + return this.httpsTransducer(imageRes.data.file.url); + } catch (error) { + throw new BadRequestException(error.message); + } + } + + private httpsTransducer(url: string) { + const protocol = new URL(url).protocol; + + return url.replace(protocol, "https:"); + } +} diff --git a/src/routes/post/dto/create-post.dto.ts b/src/routes/post/dto/create-post.dto.ts new file mode 100644 index 0000000..c9c99cb --- /dev/null +++ b/src/routes/post/dto/create-post.dto.ts @@ -0,0 +1,14 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { IsArray, IsString } from "class-validator"; + +import { IsOptionalCustom } from "@/common/decorators/is-optional.decorator"; +import { Post } from "@/routes/post/post.schema"; + +export class CreatePostDto extends PickType(Post, ["title", "content", "thumbnail"]) { + @IsOptionalCustom(IsArray(), IsString({ each: true })) + tags?: string[]; + + @IsOptionalCustom(IsString()) + series?: string; +} diff --git a/src/routes/post/dto/get-posts.dto.ts b/src/routes/post/dto/get-posts.dto.ts new file mode 100644 index 0000000..dc8eeca --- /dev/null +++ b/src/routes/post/dto/get-posts.dto.ts @@ -0,0 +1,27 @@ +import { Type } from "class-transformer"; +import { IsNumber, IsString } from "class-validator"; + +import { IsOptionalCustom } from "@/common/decorators/is-optional.decorator"; + +export class GetPostsDto { + @IsOptionalCustom(IsString()) + series?: string; + + @Type(() => Number) + @IsOptionalCustom(IsNumber()) + skip?: number; + + @Type(() => Number) + @IsOptionalCustom(IsNumber()) + limit?: number; + + @Type(() => Number) + @IsOptionalCustom(IsNumber()) + sort?: number; + + @IsOptionalCustom(IsString()) + tag?: string; + + @IsOptionalCustom(IsString()) + text?: string; +} diff --git a/src/routes/post/dto/update-post.dto.ts b/src/routes/post/dto/update-post.dto.ts new file mode 100644 index 0000000..b667b94 --- /dev/null +++ b/src/routes/post/dto/update-post.dto.ts @@ -0,0 +1,20 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { IsArray, IsString } from "class-validator"; + +import { IsOptionalCustom } from "@/common/decorators/is-optional.decorator"; +import { Post } from "@/routes/post/post.schema"; + +export class UpdatePostDto extends PickType(Post, ["title", "content", "thumbnail"]) { + @IsString() + _id: string; + + @IsOptionalCustom(IsString()) + series?: string; + + @IsOptionalCustom(IsArray(), IsString({ each: true })) + deleteTags?: string[]; + + @IsOptionalCustom(IsArray(), IsString({ each: true })) + addTags?: string[]; +} diff --git a/src/routes/post/post.controller.ts b/src/routes/post/post.controller.ts new file mode 100644 index 0000000..b8d5e3b --- /dev/null +++ b/src/routes/post/post.controller.ts @@ -0,0 +1,62 @@ +import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from "@nestjs/common"; + +import { Public } from "@/common/decorators"; +import { RealIp } from "@/common/decorators/RealIp.decorator"; +import { CreatePostDto } from "@/routes/post/dto/create-post.dto"; +import { GetPostsDto } from "@/routes/post/dto/get-posts.dto"; +import { UpdatePostDto } from "@/routes/post/dto/update-post.dto"; +import { PostService } from "@/routes/post/post.service"; + +@Controller("posts") +export class PostController { + constructor(private readonly postService: PostService) {} + + @Post() + async create(@Body() createPostDto: CreatePostDto) { + return this.postService.create(createPostDto); + } + + @Put(":nid") + async update(@Param("nid") nid: number, @Body() updatePostDto: UpdatePostDto) { + return this.postService.update(nid, updatePostDto); + } + + @Delete(":nid") + async delete(@Param("nid") nid: number) { + return this.postService.delete(nid); + } + + @Public() + @Patch(":nid/like") + async like(@Param("nid") nid: number, @RealIp() ip: string) { + return this.postService.increaseToLikes(nid, ip); + } + + @Public() + @Patch(":nid/unlike") + async unlike(@Param("nid") nid: number, @RealIp() ip: string) { + return this.postService.decreaseToLikes(nid, ip); + } + + @Public() + @Get() + async getPosts(@Query() query: GetPostsDto) { + return this.postService.getAll(query); + } + + @Public() + @Get(":nid") + async getPost(@Param("nid") nid: number, @RealIp() ip: string) { + await this.postService.increaseToViews(nid, ip); + + const post = await this.postService.getByNumId(nid, ip); + + return post; + } + + @Public() + @Get(":nid/sibling") + async getSiblingPost(@Param("nid") nid: number) { + return this.postService.getSibling(nid); + } +} diff --git a/src/routes/post/post.module.ts b/src/routes/post/post.module.ts new file mode 100644 index 0000000..996d97b --- /dev/null +++ b/src/routes/post/post.module.ts @@ -0,0 +1,24 @@ +import { Module, forwardRef } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { PostRepository } from "@/routes/post/post.repository"; +import { Post, PostSchema } from "@/routes/post/post.schema"; +import { SeriesModule } from "@/routes/series/series.module"; +import { TagModule } from "@/routes/tag/tag.module"; + +import { PostController } from "./post.controller"; +import { PostService } from "./post.service"; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: Post.name, schema: PostSchema }]), + forwardRef(() => SeriesModule), + TagModule, + SequenceModule, + ], + providers: [PostService, PostRepository], + controllers: [PostController], + exports: [PostService, PostRepository], +}) +export class PostModule {} diff --git a/src/routes/post/post.repository.ts b/src/routes/post/post.repository.ts new file mode 100644 index 0000000..685d61a --- /dev/null +++ b/src/routes/post/post.repository.ts @@ -0,0 +1,65 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { Post, PostDocument, PostModel } from "@/routes/post/post.schema"; +import { TagDocument } from "@/routes/tag/tag.schema"; +import { IUpdatePostArgs } from "@/types"; +import { POST_FIND_PROJECTION } from "@/utils/constants"; + +@Injectable() +export class PostRepository extends BaseRepository { + constructor( + @InjectModel(Post.name) private readonly postModel: PostModel, + sequenceRepository: SequenceRepository, + ) { + super(postModel, sequenceRepository); + } + + async save(post: PostDocument) { + return post.save(); + } + + async update(_id: string, updatePostDto: IUpdatePostArgs) { + return this.postModel.updateOne({ _id }, updatePostDto); + } + + async deleteSeriesInPosts(seriesId: string) { + return this.postModel.updateMany({ series: seriesId }, { series: null }); + } + + async pushTags(postId: string, tags: TagDocument[]) { + return this.postModel.updateOne( + { _id: postId, tags: { $nin: tags } }, + { $push: { tags: { $each: tags } } }, + ); + } + + async pullTags(postId: string, tags: TagDocument[]) { + return this.postModel.updateOne( + { _id: postId, tags: { $in: tags } }, + { $pull: { tags: { $in: tags } } }, + ); + } + + async increaseToLikes(nid: number, ip: string) { + return this.postModel.updateOne({ nid }, { $push: { likes: ip } }); + } + + async decreaseToLikes(nid: number, ip: string) { + return this.postModel.updateOne({ nid }, { $pull: { likes: ip } }); + } + + async increaseToViews(nid: number, ip: string) { + return this.postModel.updateOne({ nid }, { $push: { views: ip } }); + } + + async isViewed(nid: number, ip: string) { + return this.postModel.exists({ nid, views: { $in: ip } }); + } + + async getById(_id: string) { + return super.getById(_id, POST_FIND_PROJECTION, { populate: ["tags", "series"] }); + } +} diff --git a/src/routes/post/post.schema.ts b/src/routes/post/post.schema.ts new file mode 100644 index 0000000..c90b4f9 --- /dev/null +++ b/src/routes/post/post.schema.ts @@ -0,0 +1,67 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsString } from "class-validator"; +import { Document, Model, Types } from "mongoose"; + +import { BaseSchema } from "@/common/schema/base.schema"; +import { Series } from "@/routes/series/series.schema"; +import { Tag } from "@/routes/tag/tag.schema"; + +export type PostDocument = Post & Document; +export type PostModel = Model; + +@Schema({ timestamps: true }) +export class Post extends BaseSchema { + @IsString() + @Prop({ required: true }) + title!: string; + + @IsString() + @Prop({ required: true }) + content!: string; + + @IsString() + @Prop({ required: true }) + thumbnail!: string; + + @Prop({ + type: Types.ObjectId, + ref: Series.name, + required: false, + default: null, + }) + series?: Series; + + @Prop({ + type: [{ type: String }], + required: false, + default: [], + }) + likes?: string[]; + + @Prop({ + type: [{ type: String }], + required: false, + default: [], + }) + views?: string[]; + + @Prop({ + type: [{ type: Types.ObjectId }], + ref: Tag.name, + required: false, + default: [], + }) + tags?: Tag[]; + + @Prop() + isLiked?: boolean; + + @Prop() + likeCount?: number; + + @Prop() + viewCount?: number; +} + +export const PostSchema = SchemaFactory.createForClass(Post); diff --git a/src/routes/post/post.service.ts b/src/routes/post/post.service.ts new file mode 100644 index 0000000..3c15839 --- /dev/null +++ b/src/routes/post/post.service.ts @@ -0,0 +1,191 @@ +import { BadRequestException, Inject, Injectable, forwardRef } from "@nestjs/common"; + +import { FilterQuery } from "mongoose"; + +import { Transactional } from "@/common/decorators/transaction.decorator"; +import { CreatePostDto } from "@/routes/post/dto/create-post.dto"; +import { GetPostsDto } from "@/routes/post/dto/get-posts.dto"; +import { UpdatePostDto } from "@/routes/post/dto/update-post.dto"; +import { PostRepository } from "@/routes/post/post.repository"; +import { PostDocument } from "@/routes/post/post.schema"; +import { SeriesService } from "@/routes/series/series.service"; +import { TagService } from "@/routes/tag/tag.service"; +import { POST_ERROR, POST_FIND_PROJECTION } from "@/utils/constants"; +import { filterQueryByPosts } from "@/utils/filterQueryByPosts"; + +@Injectable() +export class PostService { + constructor( + private readonly postRepository: PostRepository, + private readonly tagService: TagService, + @Inject(forwardRef(() => SeriesService)) private readonly seriesService: SeriesService, + ) {} + + @Transactional() + async create(createPostDto: CreatePostDto) { + const { tags: tagNames, series: seriesName, ...rest } = createPostDto; + + try { + const post = await this.postRepository.create(rest); + + if (seriesName) { + post.series = await this.seriesService.pushPostIdInSeries(seriesName, post._id); + } + + if (tagNames?.length) { + post.tags = await this.tagService.pushPostIdInTags(tagNames, post._id); + } + + await this.postRepository.save(post); + + return post; + } catch (error) { + throw new BadRequestException(error?.massage || POST_ERROR.FAIL_CREATE); + } + } + + @Transactional() + async delete(nid: number) { + const post = await this.postRepository.getOne({ nid }, {}, { populate: ["tags", "series"] }); + const _id = post._id; + + if (!post) { + throw new BadRequestException(POST_ERROR.NOT_FOUND); + } + + const tagNames = post.tags.map((tag) => tag.name); + + if (post.series) await this.seriesService.pullPostIdInSeries(post.series.name, _id); + if (tagNames.length) await this.tagService.pullPostIdInTags(tagNames, _id); + await this.postRepository.delete(_id); + + return post.nid; + } + + @Transactional() + async update(nid: number, updatePostDto: UpdatePostDto) { + const { addTags = [], deleteTags = [], series: seriesName, ...rest } = updatePostDto; + + const post = await this.postRepository.getOne({ nid }); + const _id = post._id; + + if (!post) { + throw new BadRequestException(POST_ERROR.NOT_FOUND); + } + + const input = { ...rest, series: post.series ?? null }; + const prevSeriesName = post.series?.name ?? null; + + try { + if (prevSeriesName && prevSeriesName !== seriesName) { + input.series = null; + await this.seriesService.pullPostIdInSeries(prevSeriesName, _id); + } + + if (seriesName && prevSeriesName !== seriesName) { + const newSeries = await this.seriesService.pushPostIdInSeries(seriesName, _id); + + input.series = newSeries._id; + } + + await this.postRepository.update(_id, input); + + const [dTags, aTags] = await Promise.all([ + this.tagService.pullPostIdInTags(deleteTags, _id), + this.tagService.pushPostIdInTags(addTags, _id), + ]); + + await this.postRepository.pushTags(_id, aTags); + await this.postRepository.pullTags(_id, dTags); + } catch (error) { + throw new BadRequestException(error?.massage || POST_ERROR.FAIL_UPDATE); + } + + return this.postRepository.getOne({ nid }); + } + + @Transactional() + async increaseToLikes(nid: number, ip: string) { + await this.existPost({ nid }); + + const projection = { ...POST_FIND_PROJECTION, isLiked: { $in: [ip, "$likes"] } }; + + const post = await this.postRepository.getOne({ nid }, projection); + + if (post.isLiked) { + throw new BadRequestException(POST_ERROR.ALREADY_LIKED); + } + + await this.postRepository.increaseToLikes(nid, ip); + + return nid; + } + + @Transactional() + async decreaseToLikes(nid: number, ip: string) { + await this.existPost({ nid }); + + await this.postRepository.decreaseToLikes(nid, ip); + + return nid; + } + + @Transactional() + async increaseToViews(nid: number, ip: string) { + await this.existPost({ nid }); + + const post = await this.postRepository.isViewed(nid, ip); + + if (post) return; + + await this.postRepository.increaseToViews(nid, ip); + } + + async getAll(getPostsDto: GetPostsDto) { + const { skip = 0, limit = 10, sort = -1, ...rest } = getPostsDto; + + const filter = filterQueryByPosts(rest); + const options = { skip, limit, sort: { _id: sort }, populate: ["tags", "series"] }; + + return this.postRepository.getAll(filter, POST_FIND_PROJECTION, options); + } + + async getByNumId(nid: number, ip: string) { + const projection = { ...POST_FIND_PROJECTION, isLiked: { $in: [ip, "$likes"] } }; + + const post = await this.postRepository.getOne({ nid }, projection, { + populate: ["tags", "series"], + }); + + if (!post) { + throw new BadRequestException(POST_ERROR.NOT_FOUND); + } + + return post; + } + + async getById(_id: string) { + return this.postRepository.getById(_id); + } + + async getSibling(targetNid: number) { + const projection = { _id: 1, nid: 1, title: 1 }; + + const [prev, next] = await Promise.all([ + this.postRepository.getOne({ nid: { $lt: targetNid } }, projection, { sort: { nid: -1 } }), + this.postRepository.getOne({ nid: { $gt: targetNid } }, projection), + ]); + + return { prev, next }; + } + + async existPost(filter: FilterQuery) { + const post = await this.postRepository.getOne(filter); + + if (!post) { + throw new BadRequestException(POST_ERROR.NOT_FOUND); + } + + return true; + } +} diff --git a/src/routes/project/dto/create-project.dto.ts b/src/routes/project/dto/create-project.dto.ts new file mode 100644 index 0000000..2dfbea9 --- /dev/null +++ b/src/routes/project/dto/create-project.dto.ts @@ -0,0 +1,14 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { Project } from "@/routes/project/project.schema"; + +export class CreateProjectDto extends PickType(Project, [ + "title", + "description", + "content", + "thumbnail", + "github", + "start", + "end", + "page", +]) {} diff --git a/src/routes/project/dto/update-project.dto.ts b/src/routes/project/dto/update-project.dto.ts new file mode 100644 index 0000000..45eab74 --- /dev/null +++ b/src/routes/project/dto/update-project.dto.ts @@ -0,0 +1,3 @@ +import { CreateProjectDto } from "@/routes/project/dto/create-project.dto"; + +export class UpdateProjectDto extends CreateProjectDto {} diff --git a/src/routes/project/project.controller.ts b/src/routes/project/project.controller.ts new file mode 100644 index 0000000..977dbdf --- /dev/null +++ b/src/routes/project/project.controller.ts @@ -0,0 +1,48 @@ +import { Body, Controller, Delete, Get, Param, Post, Put } from "@nestjs/common"; + +import { Public } from "@/common/decorators"; +import { CreateProjectDto } from "@/routes/project/dto/create-project.dto"; +import { UpdateProjectDto } from "@/routes/project/dto/update-project.dto"; +import { ProjectService } from "@/routes/project/project.service"; + +@Controller("projects") +export class ProjectController { + constructor(private readonly projectService: ProjectService) {} + + @Post() + async create(@Body() createProjectDto: CreateProjectDto) { + const project = await this.projectService.create(createProjectDto); + + return project; + } + + @Put(":nid") + async update(@Param("nid") nid: number, @Body() updateProjectDto: UpdateProjectDto) { + const project = await this.projectService.update(nid, updateProjectDto); + + return project; + } + + @Delete(":nid") + async delete(@Param("nid") nid: number) { + await this.projectService.delete(nid); + + return true; + } + + @Public() + @Get(":nid") + async getByNumId(@Param("nid") nid: number) { + const project = await this.projectService.getByNumId(nid); + + return project; + } + + @Public() + @Get() + async getAll() { + const projects = await this.projectService.getAll(); + + return projects; + } +} diff --git a/src/routes/project/project.module.ts b/src/routes/project/project.module.ts new file mode 100644 index 0000000..13602bc --- /dev/null +++ b/src/routes/project/project.module.ts @@ -0,0 +1,20 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { ProjectRepository } from "@/routes/project/project.repository"; +import { Project, ProjectSchema } from "@/routes/project/project.schema"; + +import { ProjectController } from "./project.controller"; +import { ProjectService } from "./project.service"; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: Project.name, schema: ProjectSchema }]), + SequenceModule, + ], + providers: [ProjectService, ProjectRepository], + controllers: [ProjectController], + exports: [ProjectService], +}) +export class ProjectModule {} diff --git a/src/routes/project/project.repository.ts b/src/routes/project/project.repository.ts new file mode 100644 index 0000000..96bc34f --- /dev/null +++ b/src/routes/project/project.repository.ts @@ -0,0 +1,16 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { Project, ProjectDocument, ProjectModel } from "@/routes/project/project.schema"; + +@Injectable() +export class ProjectRepository extends BaseRepository { + constructor( + @InjectModel(Project.name) private readonly projectModel: ProjectModel, + sequenceRepository: SequenceRepository, + ) { + super(projectModel, sequenceRepository); + } +} diff --git a/src/routes/project/project.schema.ts b/src/routes/project/project.schema.ts new file mode 100644 index 0000000..d8115eb --- /dev/null +++ b/src/routes/project/project.schema.ts @@ -0,0 +1,47 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsDateString, IsString } from "class-validator"; +import { Document, Model } from "mongoose"; + +import { IsOptionalCustom } from "@/common/decorators/is-optional.decorator"; +import { BaseSchema } from "@/common/schema/base.schema"; + +export type ProjectDocument = Project & Document; +export type ProjectModel = Model; + +@Schema({ timestamps: true }) +export class Project extends BaseSchema { + @IsString() + @Prop({ required: true }) + title!: string; + + @IsString() + @Prop({ required: true }) + description!: string; + + @IsString() + @Prop({ required: true }) + content!: string; + + @IsString() + @Prop({ required: true }) + thumbnail!: string; + + @IsString() + @Prop({ required: true }) + github!: string; + + @IsOptionalCustom(IsString()) + @Prop({ required: false, default: null }) + page: string | null; + + @IsDateString() + @Prop({ required: true }) + start!: string; + + @IsOptionalCustom(IsDateString()) + @Prop({ required: false, default: null }) + end?: string; +} + +export const ProjectSchema = SchemaFactory.createForClass(Project); diff --git a/src/routes/project/project.service.ts b/src/routes/project/project.service.ts new file mode 100644 index 0000000..3bc9357 --- /dev/null +++ b/src/routes/project/project.service.ts @@ -0,0 +1,41 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; + +import { CreateProjectDto } from "@/routes/project/dto/create-project.dto"; +import { UpdateProjectDto } from "@/routes/project/dto/update-project.dto"; +import { ProjectRepository } from "@/routes/project/project.repository"; +import { PROJECT_ERROR } from "@/utils/constants"; + +@Injectable() +export class ProjectService { + constructor(private readonly projectRepository: ProjectRepository) {} + + async create(dto: CreateProjectDto) { + return this.projectRepository.create(dto); + } + + async update(nid: number, dto: UpdateProjectDto) { + const { _id } = await this.getByNumId(nid); + + return this.projectRepository.findOneAndUpdate({ _id }, dto); + } + + async delete(nid: number) { + const { _id } = await this.getByNumId(nid); + + return this.projectRepository.findOneAndDelete({ _id }); + } + + async getByNumId(nid: number) { + const project = await this.projectRepository.getOne({ nid }); + + if (!project) { + throw new BadRequestException(PROJECT_ERROR.NOT_FOUND); + } + + return project; + } + + async getAll() { + return this.projectRepository.getAll({}, {}, { sort: { _id: -1 } }); + } +} diff --git a/src/routes/series/dto/create-series.dto.ts b/src/routes/series/dto/create-series.dto.ts new file mode 100644 index 0000000..4e43144 --- /dev/null +++ b/src/routes/series/dto/create-series.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { Series } from "@/routes/series/series.schema"; + +export class CreateSeriesDto extends PickType(Series, ["name", "thumbnail"]) {} diff --git a/src/routes/series/dto/update-series.dto.ts b/src/routes/series/dto/update-series.dto.ts new file mode 100644 index 0000000..1f1dafb --- /dev/null +++ b/src/routes/series/dto/update-series.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from "@nestjs/mapped-types"; + +import { CreateSeriesDto } from "@/routes/series/dto/create-series.dto"; + +export class UpdateSeriesDto extends PartialType(CreateSeriesDto) {} diff --git a/src/routes/series/series.controller.ts b/src/routes/series/series.controller.ts new file mode 100644 index 0000000..b570f79 --- /dev/null +++ b/src/routes/series/series.controller.ts @@ -0,0 +1,40 @@ +import { Body, Controller, Delete, Get, Param, Put } from "@nestjs/common"; + +import { Public } from "@/common/decorators"; +import { UpdateSeriesDto } from "@/routes/series/dto/update-series.dto"; +import { SeriesService } from "@/routes/series/series.service"; + +@Controller("series") +export class SeriesController { + constructor(private readonly seriesService: SeriesService) {} + + @Public() + @Get() + async getAll() { + const series = await this.seriesService.getAll(); + + return series; + } + + @Public() + @Get(":nid") + async getByNumId(@Param("nid") nid: number) { + const series = await this.seriesService.getByNumId(nid); + + return series; + } + + @Put(":nid") + async update(@Param("nid") nid: number, @Body() updateSeriesDto: UpdateSeriesDto) { + const series = await this.seriesService.update(nid, updateSeriesDto); + + return series; + } + + @Delete(":nid") + async delete(@Param("nid") nid: number) { + await this.seriesService.delete(nid); + + return true; + } +} diff --git a/src/routes/series/series.module.ts b/src/routes/series/series.module.ts new file mode 100644 index 0000000..e48258e --- /dev/null +++ b/src/routes/series/series.module.ts @@ -0,0 +1,22 @@ +import { Module, forwardRef } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { PostModule } from "@/routes/post/post.module"; +import { SeriesRepository } from "@/routes/series/series.repository"; +import { Series, SeriesSchema } from "@/routes/series/series.schema"; + +import { SeriesController } from "./series.controller"; +import { SeriesService } from "./series.service"; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: Series.name, schema: SeriesSchema }]), + forwardRef(() => PostModule), + SequenceModule, + ], + providers: [SeriesService, SeriesRepository], + controllers: [SeriesController], + exports: [SeriesService], +}) +export class SeriesModule {} diff --git a/src/routes/series/series.repository.ts b/src/routes/series/series.repository.ts new file mode 100644 index 0000000..eacccc5 --- /dev/null +++ b/src/routes/series/series.repository.ts @@ -0,0 +1,55 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { Series, SeriesDocument, SeriesModel } from "@/routes/series/series.schema"; +import { SERIES_FIND_PROJECTION } from "@/utils/constants"; + +@Injectable() +export class SeriesRepository extends BaseRepository { + constructor( + @InjectModel(Series.name) private readonly seriesModel: SeriesModel, + sequenceRepository: SequenceRepository, + ) { + super(seriesModel, sequenceRepository); + } + + async pushPostIdInSeries(seriesId: string, postId: string) { + return this.seriesModel.updateOne( + { + _id: seriesId, + posts: { $nin: postId }, + }, + { $push: { posts: postId } }, + ); + } + + async pullPostIdInSeries(seriesId: string, postId: string) { + return this.seriesModel.updateOne( + { + _id: seriesId, + posts: { $in: [postId] }, + }, + { $pull: { posts: postId } }, + ); + } + + async getAll() { + return this.seriesModel.find({}, { ...SERIES_FIND_PROJECTION }); + } + + async getById(_id: string) { + return this.seriesModel.findById(_id, SERIES_FIND_PROJECTION); + } + + async findOrCreate(name: string) { + const series = await this.getOne({ name }); + + if (series) { + return series; + } + + return this.create({ name }); + } +} diff --git a/src/routes/series/series.schema.ts b/src/routes/series/series.schema.ts new file mode 100644 index 0000000..80e8c8e --- /dev/null +++ b/src/routes/series/series.schema.ts @@ -0,0 +1,31 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsString } from "class-validator"; +import { Document, Model, Types } from "mongoose"; + +import { BaseSchema } from "@/common/schema/base.schema"; +import { Post } from "@/routes/post/post.schema"; + +export type SeriesDocument = Series & Document; +export type SeriesModel = Model; + +@Schema({ timestamps: true }) +export class Series extends BaseSchema { + @IsString() + @Prop({ required: true, unique: true }) + name!: string; + + @IsString() + @Prop({ required: false, default: null }) + thumbnail?: string; + + @Prop({ + ref: "Post", + type: [{ type: Types.ObjectId, ref: "Post" }], + default: [], + required: false, + }) + posts: Post[]; +} + +export const SeriesSchema = SchemaFactory.createForClass(Series); diff --git a/src/routes/series/series.service.ts b/src/routes/series/series.service.ts new file mode 100644 index 0000000..9efb3a3 --- /dev/null +++ b/src/routes/series/series.service.ts @@ -0,0 +1,84 @@ +import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; + +import { Transactional } from "@/common/decorators/transaction.decorator"; +import { PostRepository } from "@/routes/post/post.repository"; +import { CreateSeriesDto } from "@/routes/series/dto/create-series.dto"; +import { UpdateSeriesDto } from "@/routes/series/dto/update-series.dto"; +import { SeriesRepository } from "@/routes/series/series.repository"; +import { SERIES_ERROR, SERIES_FIND_OPTIONS, SERIES_FIND_PROJECTION } from "@/utils/constants"; + +@Injectable() +export class SeriesService { + constructor( + private readonly seriesRepository: SeriesRepository, + private readonly postRepository: PostRepository, + ) {} + + @Transactional() + async create(createSeriesDto: CreateSeriesDto) { + const { name } = createSeriesDto; + + const prevSeries = await this.seriesRepository.getOne({ name }); + + if (prevSeries) { + throw new BadRequestException(SERIES_ERROR.ALREADY_EXISTS); + } + + return this.seriesRepository.create(createSeriesDto); + } + + @Transactional() + async update(nid: number, updateSeriesDto: UpdateSeriesDto) { + const { _id } = await this.getByNumId(nid); + + return this.seriesRepository.findOneAndUpdate({ _id }, updateSeriesDto); + } + + @Transactional() + async pushPostIdInSeries(name: string, postId: string) { + const series = await this.seriesRepository.findOrCreate(name); + + await this.seriesRepository.pushPostIdInSeries(series._id, postId); + + return series; + } + + @Transactional() + async pullPostIdInSeries(name: string, postId: string) { + const series = await this.seriesRepository.getOne({ name }); + + if (!series) { + throw new BadRequestException(SERIES_ERROR.NOT_FOUND); + } + + await this.seriesRepository.pullPostIdInSeries(series._id, postId); + } + + @Transactional() + async delete(nid: number) { + const { _id } = await this.getByNumId(nid); + + await this.postRepository.deleteSeriesInPosts(_id); + await this.seriesRepository.delete(_id); + + return true; + } + + async getAll() { + return this.seriesRepository.getAll(); + } + + async getByNumId(nid: number) { + const series = await this.seriesRepository.getOne( + { nid }, + SERIES_FIND_PROJECTION, + SERIES_FIND_OPTIONS, + ); + + if (!series) { + throw new NotFoundException(SERIES_ERROR.NOT_FOUND); + } + + return series; + } +} diff --git a/src/routes/skill/dto/create-skill.dto.ts b/src/routes/skill/dto/create-skill.dto.ts new file mode 100644 index 0000000..9469ef4 --- /dev/null +++ b/src/routes/skill/dto/create-skill.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { Skill } from "@/routes/skill/skill.schema"; + +export class CreateSkillDto extends PickType(Skill, ["name", "type", "icon", "description"]) {} diff --git a/src/routes/skill/dto/update-skill.dto.ts b/src/routes/skill/dto/update-skill.dto.ts new file mode 100644 index 0000000..4697e76 --- /dev/null +++ b/src/routes/skill/dto/update-skill.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from "@nestjs/mapped-types"; + +import { CreateSkillDto } from "@/routes/skill/dto/create-skill.dto"; + +export class UpdateSkillDto extends PartialType(CreateSkillDto) {} diff --git a/src/routes/skill/skill.controller.ts b/src/routes/skill/skill.controller.ts new file mode 100644 index 0000000..936d57d --- /dev/null +++ b/src/routes/skill/skill.controller.ts @@ -0,0 +1,35 @@ +import { Body, Controller, Delete, Get, Param, Post, Put } from "@nestjs/common"; + +import { Public } from "@/common/decorators"; +import { ValidateObjectIdPipe } from "@/common/pipes/validate-objectid.pipe"; +import { CreateSkillDto } from "@/routes/skill/dto/create-skill.dto"; +import { UpdateSkillDto } from "@/routes/skill/dto/update-skill.dto"; +import { SkillService } from "@/routes/skill/skill.service"; + +@Controller("skills") +export class SkillController { + constructor(private readonly skillService: SkillService) {} + + @Post() + async create(@Body() data: CreateSkillDto) { + return this.skillService.create(data); + } + + @Put(":_id") + async update(@Param("_id", ValidateObjectIdPipe) _id: string, @Body() data: UpdateSkillDto) { + return this.skillService.update(_id, { ...data }); + } + + @Delete(":_id") + async delete(@Param("_id", ValidateObjectIdPipe) _id: string) { + await this.skillService.delete(_id); + + return true; + } + + @Public() + @Get() + async getAll() { + return this.skillService.getAll(); + } +} diff --git a/src/routes/skill/skill.module.ts b/src/routes/skill/skill.module.ts new file mode 100644 index 0000000..54d4ecd --- /dev/null +++ b/src/routes/skill/skill.module.ts @@ -0,0 +1,17 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { SkillRepository } from "@/routes/skill/skill.repository"; +import { Skill, SkillSchema } from "@/routes/skill/skill.schema"; + +import { SkillController } from "./skill.controller"; +import { SkillService } from "./skill.service"; + +@Module({ + imports: [MongooseModule.forFeature([{ name: Skill.name, schema: SkillSchema }]), SequenceModule], + providers: [SkillService, SkillRepository], + controllers: [SkillController], + exports: [SkillService], +}) +export class SkillModule {} diff --git a/src/routes/skill/skill.repository.ts b/src/routes/skill/skill.repository.ts new file mode 100644 index 0000000..f496899 --- /dev/null +++ b/src/routes/skill/skill.repository.ts @@ -0,0 +1,37 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { Skill, SkillDocument, SkillModel } from "@/routes/skill/skill.schema"; +import { SkillType, TFilteredSkills } from "@/types"; + +@Injectable() +export class SkillRepository extends BaseRepository { + constructor( + @InjectModel(Skill.name) private readonly skillModel: SkillModel, + sequenceRepository: SequenceRepository, + ) { + super(skillModel, sequenceRepository); + } + + async getAllToSkillType() { + const skills = await this.skillModel.aggregate([ + { $group: { _id: "$type", skills: { $push: "$$ROOT" } } }, + ]); + + return skills.reduce( + (acc, { _id, skills }) => { + acc[_id] = skills; + + return acc; + }, + { + [SkillType.FRONT_END]: [], + [SkillType.BACK_END]: [], + [SkillType.DEV_OPS]: [], + [SkillType.LANGUAGE]: [], + } as TFilteredSkills, + ); + } +} diff --git a/src/routes/skill/skill.schema.ts b/src/routes/skill/skill.schema.ts new file mode 100644 index 0000000..0262048 --- /dev/null +++ b/src/routes/skill/skill.schema.ts @@ -0,0 +1,31 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsEnum, IsString } from "class-validator"; +import { Document, Model } from "mongoose"; + +import { BaseSchema } from "@/common/schema/base.schema"; +import { SkillType } from "@/types"; + +export type SkillDocument = Skill & Document; +export type SkillModel = Model; + +@Schema({ timestamps: true }) +export class Skill extends BaseSchema { + @IsString() + @Prop({ required: true, unique: true }) + name!: string; + + @IsString({}) + @Prop({ required: false, default: "" }) + description!: string; + + @IsEnum(SkillType) + @Prop({ required: true, enum: SkillType }) + type!: SkillType; + + @IsString() + @Prop({ required: true }) + icon!: string; +} + +export const SkillSchema = SchemaFactory.createForClass(Skill); diff --git a/src/routes/skill/skill.service.ts b/src/routes/skill/skill.service.ts new file mode 100644 index 0000000..bb41f6e --- /dev/null +++ b/src/routes/skill/skill.service.ts @@ -0,0 +1,58 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; + +import { FilterQuery } from "mongoose"; + +import { CreateSkillDto } from "@/routes/skill/dto/create-skill.dto"; +import { UpdateSkillDto } from "@/routes/skill/dto/update-skill.dto"; +import { SkillRepository } from "@/routes/skill/skill.repository"; +import { SkillDocument } from "@/routes/skill/skill.schema"; +import { SKILL_ERROR } from "@/utils/constants"; + +@Injectable() +export class SkillService { + constructor(private readonly skillRepository: SkillRepository) {} + + async create(data: CreateSkillDto) { + await this.checkToExist({ name: data.name }); + + return await this.skillRepository.create(data); + } + + async update(_id: string, data: UpdateSkillDto) { + await this.checkToExistById(_id); + + await this.checkToExist({ name: data.name, _id: { $ne: _id } }); + + return this.skillRepository.findOneAndUpdate({ _id }, data); + } + + async delete(_id: string) { + await this.checkToExistById(_id); + + return this.skillRepository.delete(_id); + } + + async getAll() { + return await this.skillRepository.getAllToSkillType(); + } + + async checkToExistById(_id: string) { + const skill = await this.skillRepository.getById(_id); + + if (!skill) { + throw new BadRequestException(SKILL_ERROR.NOT_FOUND); + } + + return true; + } + + async checkToExist(query: FilterQuery) { + const skill = await this.skillRepository.getOne(query); + + if (skill) { + throw new BadRequestException(SKILL_ERROR.ALREADY_EXISTS); + } + + return true; + } +} diff --git a/src/routes/tag/tag.controller.ts b/src/routes/tag/tag.controller.ts new file mode 100644 index 0000000..c5f782c --- /dev/null +++ b/src/routes/tag/tag.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Param } from "@nestjs/common"; + +import { Public } from "@/common/decorators"; +import { TagService } from "@/routes/tag/tag.service"; + +@Controller("tags") +export class TagController { + constructor(private readonly tagService: TagService) {} + + @Public() + @Get() + async getAll() { + return this.tagService.getAll(); + } + + @Public() + @Get(":name") + async getByName(@Param("name") name: string) { + const tag = await this.tagService.getByName(name); + + return tag; + } +} diff --git a/src/routes/tag/tag.module.ts b/src/routes/tag/tag.module.ts new file mode 100644 index 0000000..311135d --- /dev/null +++ b/src/routes/tag/tag.module.ts @@ -0,0 +1,16 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { TagController } from "@/routes/tag/tag.controller"; +import { TagRepository } from "@/routes/tag/tag.repository"; +import { Tag, TagSchema } from "@/routes/tag/tag.schema"; +import { TagService } from "@/routes/tag/tag.service"; + +@Module({ + imports: [MongooseModule.forFeature([{ name: Tag.name, schema: TagSchema }]), SequenceModule], + controllers: [TagController], + providers: [TagService, TagRepository], + exports: [TagService, TagRepository], +}) +export class TagModule {} diff --git a/src/routes/tag/tag.repository.ts b/src/routes/tag/tag.repository.ts new file mode 100644 index 0000000..27b1b21 --- /dev/null +++ b/src/routes/tag/tag.repository.ts @@ -0,0 +1,54 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { Tag, TagDocument, TagModel } from "@/routes/tag/tag.schema"; +import { GET_TAGS_OPTIONS } from "@/utils/constants"; + +@Injectable() +export class TagRepository extends BaseRepository { + constructor( + @InjectModel(Tag.name) private readonly tagModel: TagModel, + sequenceRepository: SequenceRepository, + ) { + super(tagModel, sequenceRepository); + } + + async addPostIdInTags(tags: TagDocument[], postId: string) { + return await this.tagModel.updateMany( + { posts: { $ne: postId }, _id: { $in: tags.map((tag) => tag._id) } }, + { $push: { posts: postId } }, + ); + } + + async pullPostIdInTags(tagNames: string[], postId: string) { + await this.tagModel + .updateMany({ name: { $in: tagNames }, posts: { $in: postId } }, { $pull: { posts: postId } }) + .exec(); + + await this.deleteTagsByEmptyPosts(); + } + + async pullPostIdByPostId(postId: string) { + await this.tagModel.updateMany({ posts: { $in: postId } }, { $pull: { posts: postId } }); + + await this.deleteTagsByEmptyPosts(); + } + + async deleteTagsByEmptyPosts() { + await this.tagModel.deleteMany({ posts: { $size: 0 } }); + } + + async findOrCreate(name: string) { + const tag = await this.tagModel.findOne({ name }); + + if (tag) return tag; + + return super.create({ name }); + } + + async getAllToA() { + return this.tagModel.aggregate(GET_TAGS_OPTIONS).exec(); + } +} diff --git a/src/routes/tag/tag.schema.ts b/src/routes/tag/tag.schema.ts new file mode 100644 index 0000000..cc5f63f --- /dev/null +++ b/src/routes/tag/tag.schema.ts @@ -0,0 +1,27 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsString } from "class-validator"; +import { Document, Model, Types } from "mongoose"; + +import { BaseSchema } from "@/common/schema/base.schema"; +import { Post } from "@/routes/post/post.schema"; + +export type TagDocument = Tag & Document; +export type TagModel = Model; + +@Schema({ timestamps: true }) +export class Tag extends BaseSchema { + @IsString() + @Prop({ required: true, unique: true }) + name!: string; + + @Prop({ + ref: "Post", + type: [{ type: Types.ObjectId, ref: "Post" }], + default: [], + required: false, + }) + posts?: Post[]; +} + +export const TagSchema = SchemaFactory.createForClass(Tag); diff --git a/src/routes/tag/tag.service.ts b/src/routes/tag/tag.service.ts new file mode 100644 index 0000000..09c3af7 --- /dev/null +++ b/src/routes/tag/tag.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from "@nestjs/common"; + +import { Transactional } from "@/common/decorators/transaction.decorator"; +import { TagRepository } from "@/routes/tag/tag.repository"; +import { TagDocument } from "@/routes/tag/tag.schema"; + +@Injectable() +export class TagService { + constructor(private readonly tagRepository: TagRepository) {} + + async getAll() { + return this.tagRepository.getAllToA(); + } + + async getByName(name: string) { + return this.tagRepository.getOne( + { name }, + { name: 1, postCount: { $size: "$posts" } }, + { populate: "posts" }, + ); + } + + @Transactional() + async pushPostIdInTags(tagNames: string[], postId: string) { + const tags: TagDocument[] = []; + + for (const tagName of tagNames) { + const tag = await this.tagRepository.findOrCreate(tagName); + tags.push(tag); + } + + await this.tagRepository.addPostIdInTags(tags, postId); + + return tags; + } + + @Transactional() + async pullPostIdInTags(tagNames: string[], postId: string) { + await this.tagRepository.pullPostIdInTags(tagNames, postId); + + return this.tagRepository.getAll({ name: { $in: tagNames } }); + } + + @Transactional() + async pullPostIdByPostId(postId: string) { + await this.tagRepository.pullPostIdByPostId(postId); + } +} diff --git a/src/routes/user/dto/create-user.dto.ts b/src/routes/user/dto/create-user.dto.ts new file mode 100644 index 0000000..2893896 --- /dev/null +++ b/src/routes/user/dto/create-user.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from "@nestjs/mapped-types"; + +import { User } from "@/routes/user/user.schema"; + +export class CreateUserDTO extends PickType(User, ["userId", "password", "username"]) {} diff --git a/src/routes/user/user.controller.ts b/src/routes/user/user.controller.ts new file mode 100644 index 0000000..14aa18e --- /dev/null +++ b/src/routes/user/user.controller.ts @@ -0,0 +1,26 @@ +import { Body, Controller, Get, Post } from "@nestjs/common"; + +import { Public, User } from "@/common/decorators"; +import { CreateUserDTO } from "@/routes/user/dto/create-user.dto"; +import { UserService } from "@/routes/user/user.service"; +import { TTokenUser } from "@/types"; + +@Controller("users") +export class UserController { + constructor(private readonly userService: UserService) {} + + @Public() + @Post() + async createUser(@Body() input: CreateUserDTO) { + const { username } = await this.userService.create(input); + + return { username }; + } + + @Get() + async getUser(@User() _user: TTokenUser) { + const { username } = await this.userService.getById(_user._id); + + return { username }; + } +} diff --git a/src/routes/user/user.module.ts b/src/routes/user/user.module.ts new file mode 100644 index 0000000..dfe741c --- /dev/null +++ b/src/routes/user/user.module.ts @@ -0,0 +1,16 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; + +import { SequenceModule } from "@/common/sequence/sequence.module"; +import { UserController } from "@/routes/user/user.controller"; +import { UserRepository } from "@/routes/user/user.repository"; +import { User, UserSchema } from "@/routes/user/user.schema"; +import { UserService } from "@/routes/user/user.service"; + +@Module({ + imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), SequenceModule], + controllers: [UserController], + exports: [UserService], + providers: [UserController, UserService, UserRepository], +}) +export class UserModule {} diff --git a/src/routes/user/user.repository.ts b/src/routes/user/user.repository.ts new file mode 100644 index 0000000..bc537d3 --- /dev/null +++ b/src/routes/user/user.repository.ts @@ -0,0 +1,27 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; + +import * as bcrypt from "bcryptjs"; + +import { BaseRepository } from "@/common/repository/base.repository"; +import { SequenceRepository } from "@/common/sequence/sequence.repository"; +import { CreateUserDTO } from "@/routes/user/dto/create-user.dto"; +import { User, UserDocument, UserModel } from "@/routes/user/user.schema"; + +const BCRYPT_SALT = 10; + +@Injectable() +export class UserRepository extends BaseRepository { + constructor( + @InjectModel(User.name) private readonly userModel: UserModel, + sequenceRepository: SequenceRepository, + ) { + super(userModel, sequenceRepository); + } + + async create(createUserDto: CreateUserDTO) { + createUserDto.password = await bcrypt.hash(createUserDto.password, BCRYPT_SALT); + + return super.create(createUserDto); + } +} diff --git a/src/routes/user/user.schema.ts b/src/routes/user/user.schema.ts new file mode 100644 index 0000000..f8401eb --- /dev/null +++ b/src/routes/user/user.schema.ts @@ -0,0 +1,30 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +import { IsString } from "class-validator"; +import { Document, Model } from "mongoose"; + +import { BaseSchema } from "@/common/schema/base.schema"; + +export type UserDocument = User & Document; +export type UserModel = Model; + +@Schema({ timestamps: true }) +export class User extends BaseSchema { + @IsString() + @Prop({ required: true }) + username!: string; + + @IsString() + @Prop({ required: true }) + userId!: string; + + @IsString() + @Prop({ required: true }) + password!: string; + + @IsString() + @Prop({ default: null }) + refreshToken?: string; +} + +export const UserSchema = SchemaFactory.createForClass(User); diff --git a/src/routes/user/user.service.ts b/src/routes/user/user.service.ts new file mode 100644 index 0000000..4712b9d --- /dev/null +++ b/src/routes/user/user.service.ts @@ -0,0 +1,33 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; + +import { CreateUserDTO } from "@/routes/user/dto/create-user.dto"; +import { UserRepository } from "@/routes/user/user.repository"; +import { USER_ERROR } from "@/utils/constants"; + +@Injectable() +export class UserService { + constructor(private readonly userRepository: UserRepository) {} + + async create(createUserDto: CreateUserDTO) { + const userId = createUserDto.userId; + const user = await this.userRepository.getOne({ userId }); + + if (user) { + throw new BadRequestException(USER_ERROR.ALREADY_EXISTS); + } + + return this.userRepository.create(createUserDto); + } + + async getByUserId(userId: string) { + return this.userRepository.getOne({ userId }); + } + + async getById(_id: string) { + return this.userRepository.getById(_id, { password: 0 }); + } + + async updateRefreshToken(_id: string, refreshToken: string = null) { + return this.userRepository.update(_id, { refreshToken }); + } +} diff --git a/src/types/auth.ts b/src/types/auth.ts new file mode 100644 index 0000000..d58c849 --- /dev/null +++ b/src/types/auth.ts @@ -0,0 +1,14 @@ +import { Response } from "express"; + +const enum EJwtTokenType { + ACCESS = "access", + REFRESH = "refresh", +} + +interface IRegisterTokenInCookieArgs { + type: EJwtTokenType; + token: string; + res: Response; +} + +export { EJwtTokenType, IRegisterTokenInCookieArgs }; diff --git a/src/types/image.ts b/src/types/image.ts new file mode 100644 index 0000000..48dee1d --- /dev/null +++ b/src/types/image.ts @@ -0,0 +1,3 @@ +type TImageType = "post" | "series" | "project" | "skill"; + +export { TImageType }; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..018d09c --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,5 @@ +export * from "./user"; +export * from "./auth"; +export * from "./post"; +export * from "./image"; +export * from "./skill"; diff --git a/src/types/post.ts b/src/types/post.ts new file mode 100644 index 0000000..66352b6 --- /dev/null +++ b/src/types/post.ts @@ -0,0 +1,10 @@ +import { Series } from "@/routes/series/series.schema"; + +interface IUpdatePostArgs { + title?: string; + content?: string; + thumbnail?: string; + series?: Series | null; +} + +export { IUpdatePostArgs }; diff --git a/src/types/skill.ts b/src/types/skill.ts new file mode 100644 index 0000000..1a1f337 --- /dev/null +++ b/src/types/skill.ts @@ -0,0 +1,12 @@ +import { SkillDocument } from "@/routes/skill/skill.schema"; + +enum SkillType { + LANGUAGE = "language", + FRONT_END = "front", + BACK_END = "back", + DEV_OPS = "devops", +} + +type TFilteredSkills = Record; + +export { SkillType, TFilteredSkills }; diff --git a/src/types/user.ts b/src/types/user.ts new file mode 100644 index 0000000..7d0f751 --- /dev/null +++ b/src/types/user.ts @@ -0,0 +1,5 @@ +import { User } from "@/routes/user/user.schema"; + +type TTokenUser = Pick; + +export { TTokenUser }; diff --git a/src/utils/constants/auth.ts b/src/utils/constants/auth.ts new file mode 100644 index 0000000..cb46890 --- /dev/null +++ b/src/utils/constants/auth.ts @@ -0,0 +1,10 @@ +const IS_PUBLIC_KEY = "isPublic"; + +const AUTH_ERROR = { + NO_SIGN: "로그인이 필요합니다.", + EXPIRED_TOKEN: "인증 시간이 만료되었습니다.", + INVALID_TOKEN: "유효하지 않은 정보입니다.", + UNAUTHORIZED: "인증오류가 발생했습니다.", +}; + +export { IS_PUBLIC_KEY, AUTH_ERROR }; diff --git a/src/utils/constants/experience.ts b/src/utils/constants/experience.ts new file mode 100644 index 0000000..d08be0c --- /dev/null +++ b/src/utils/constants/experience.ts @@ -0,0 +1,6 @@ +const EXPERIENCE_ERROR = { + NOT_FOUND: "경력이 존재하지 않습니다.", + ALREADY_EXISTS: "이미 존재하는 경력입니다.", +}; + +export { EXPERIENCE_ERROR }; diff --git a/src/utils/constants/image.ts b/src/utils/constants/image.ts new file mode 100644 index 0000000..71e60dd --- /dev/null +++ b/src/utils/constants/image.ts @@ -0,0 +1,13 @@ +const IMAGE_ERROR = { + INVALID_TYPE: "image/jpeg, image/png, image/jpg 형식의 파일만 업로드 가능합니다.", + INVALID_SIZE: "이미지 파일은 10MB 이하만 업로드 가능합니다.", + INVALID_UPLOAD_TYPE: "이미지 타입이 올바르지 않습니다.", +} as const; + +const IMAGE_UPLOAD_TYPES = ["post", "series", "project", "skill"] as const; + +const IMAGE_MIME_TYPES = ["image/jpeg", "image/png", "image/jpg"] as const; + +const MAX_FILE_SIZE = 10 * 1024 * 1024; + +export { IMAGE_UPLOAD_TYPES, IMAGE_MIME_TYPES, MAX_FILE_SIZE, IMAGE_ERROR }; diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts new file mode 100644 index 0000000..95b9c2c --- /dev/null +++ b/src/utils/constants/index.ts @@ -0,0 +1,9 @@ +export * from "./user"; +export * from "./auth"; +export * from "./series"; +export * from "./post"; +export * from "./image"; +export * from "./project"; +export * from "./tag"; +export * from "./experience"; +export * from "./skill"; diff --git a/src/utils/constants/post.ts b/src/utils/constants/post.ts new file mode 100644 index 0000000..f1901af --- /dev/null +++ b/src/utils/constants/post.ts @@ -0,0 +1,22 @@ +const POST_ERROR = { + NOT_FOUND: "존재하지 않는 게시글입니다.", + FAIL_CREATE: "게시글 생성에 실패했습니다.", + FAIL_UPDATE: "게시글 수정에 실패했습니다.", + ALREADY_LIKED: "이미 좋아요를 누른 게시글입니다.", +}; + +const POST_FIND_PROJECTION = { + _id: 1, + title: 1, + content: 1, + thumbnail: 1, + nid: 1, + createdAt: 1, + updatedAt: 1, + tags: 1, + series: 1, + likeCount: { $size: "$likes" }, + viewCount: { $size: "$views" }, +}; + +export { POST_ERROR, POST_FIND_PROJECTION }; diff --git a/src/utils/constants/project.ts b/src/utils/constants/project.ts new file mode 100644 index 0000000..4d74004 --- /dev/null +++ b/src/utils/constants/project.ts @@ -0,0 +1,5 @@ +const PROJECT_ERROR = { + NOT_FOUND: "존재하지 않는 프로젝트입니다.", +}; + +export { PROJECT_ERROR }; diff --git a/src/utils/constants/series.ts b/src/utils/constants/series.ts new file mode 100644 index 0000000..3d99d10 --- /dev/null +++ b/src/utils/constants/series.ts @@ -0,0 +1,22 @@ +const SERIES_ERROR = { + BAD_REQUEST: "올바르지 않은 시리즈 정보입니다.", + NOT_FOUND: "시리즈를 찾을 수 없습니다.", + ALREADY_EXISTS: "이미 존재하는 시리즈입니다.", +}; + +const SERIES_FIND_PROJECTION = { + _id: 1, + nid: 1, + name: 1, + thumbnail: 1, + posts: 1, + postCount: { $size: "$posts" }, + updatedAt: 1, + createdAt: 1, +}; + +const SERIES_FIND_OPTIONS = { + populate: "posts", +}; + +export { SERIES_ERROR, SERIES_FIND_PROJECTION, SERIES_FIND_OPTIONS }; diff --git a/src/utils/constants/skill.ts b/src/utils/constants/skill.ts new file mode 100644 index 0000000..78ed2ff --- /dev/null +++ b/src/utils/constants/skill.ts @@ -0,0 +1,7 @@ +const SKILL_ERROR = { + NOT_FOUND: "존재하지 않는 스킬입니다.", + ALREADY_EXISTS: "이미 존재하는 스킬입니다.", + NOT_FOUND_TYPE: "존재하지 않는 스킬 타입입니다.", +}; + +export { SKILL_ERROR }; diff --git a/src/utils/constants/tag.ts b/src/utils/constants/tag.ts new file mode 100644 index 0000000..6722a54 --- /dev/null +++ b/src/utils/constants/tag.ts @@ -0,0 +1,14 @@ +import { PipelineStage } from "mongoose"; + +const TAG_ERROR = { + NOT_FOUND: "존재하지 않는 태그입니다.", + ALREADY_EXIST: "이미 존재하는 태그입니다.", +}; + +const GET_TAGS_OPTIONS: PipelineStage[] = [ + { $lookup: { from: "posts", localField: "posts", foreignField: "_id", as: "posts" } }, + { $project: { _id: 1, name: 1, createdAt: 1, updatedAt: 1, postCount: { $size: "$posts" } } }, + { $sort: { postCount: -1 } }, +]; + +export { TAG_ERROR, GET_TAGS_OPTIONS }; diff --git a/src/utils/constants/user.ts b/src/utils/constants/user.ts new file mode 100644 index 0000000..e06b23c --- /dev/null +++ b/src/utils/constants/user.ts @@ -0,0 +1,8 @@ +const USER_ERROR = { + ALREADY_EXISTS: "이미 존재하는 유저입니다.", + NOT_FOUND: "존재하지 않는 유저입니다.", + BAD_REQUEST: "올바르지 않은 유저 정보입니다.", + PASSWORD_NOT_MATCH: "비밀번호가 일치하지 않습니다.", +}; + +export { USER_ERROR }; diff --git a/src/utils/filterQueryByPosts.ts b/src/utils/filterQueryByPosts.ts new file mode 100644 index 0000000..854312a --- /dev/null +++ b/src/utils/filterQueryByPosts.ts @@ -0,0 +1,29 @@ +import { FilterQuery, Types } from "mongoose"; + +import { GetPostsDto } from "@/routes/post/dto/get-posts.dto"; +import { PostDocument } from "@/routes/post/post.schema"; + +const filterQueryByPosts = (dto: GetPostsDto) => { + const { series, tag, text } = dto; + + const query: FilterQuery = {}; + + if (series && Types.ObjectId.isValid(series)) { + query.series = Types.ObjectId.createFromHexString(series); + } + + if (tag) { + query.tags = tag; + } + + if (text) { + query.$or = [ + { title: { $regex: text, $options: "i" } }, + { content: { $regex: text, $options: "i" } }, + ]; + } + + return query; +}; + +export { filterQueryByPosts }; diff --git a/src/utils/multerOptionsFactory.ts b/src/utils/multerOptionsFactory.ts new file mode 100644 index 0000000..c14f5fe --- /dev/null +++ b/src/utils/multerOptionsFactory.ts @@ -0,0 +1,23 @@ +import { BadRequestException } from "@nestjs/common"; +import { MulterModuleOptions } from "@nestjs/platform-express"; + +import { IMAGE_ERROR, IMAGE_MIME_TYPES, MAX_FILE_SIZE } from "@/utils/constants"; + +const multerOptionsFactory = (): MulterModuleOptions => { + return { + fileFilter(req, file, callback) { + const mimeType = file.mimetype as (typeof IMAGE_MIME_TYPES)[number]; + + const index = IMAGE_MIME_TYPES.indexOf(mimeType); + + if (index === -1) { + return callback(new BadRequestException(IMAGE_ERROR.INVALID_TYPE), false); + } + + callback(null, true); + }, + limits: { fileSize: MAX_FILE_SIZE }, + }; +}; + +export { multerOptionsFactory }; diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts deleted file mode 100644 index 9919e5c..0000000 --- a/test/app.e2e-spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as request from "supertest"; - -import { INestApplication } from "@nestjs/common"; -import { Test, TestingModule } from "@nestjs/testing"; - -import { AppModule } from "./../src/app.module"; - -describe("AppController (e2e)", () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it("/ (GET)", () => { - return request(app.getHttpServer()).get("/").expect(200).expect("Hello World!"); - }); -}); diff --git a/test/e2e/post.controller.e2e-spec.ts b/test/e2e/post.controller.e2e-spec.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/jest-e2e.json b/test/jest-e2e.json index e9d912f..4cab670 100644 --- a/test/jest-e2e.json +++ b/test/jest-e2e.json @@ -1,9 +1,13 @@ { "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", + "rootDir": "..", "testEnvironment": "node", "testRegex": ".e2e-spec.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" + }, + "moduleNameMapper": { + "^@/(.*)$": "/src/$1", + "^test/(.*)$": "/test/$1" } } diff --git a/test/routes/auth/auth.controller.spec.ts b/test/routes/auth/auth.controller.spec.ts new file mode 100644 index 0000000..4cdb718 --- /dev/null +++ b/test/routes/auth/auth.controller.spec.ts @@ -0,0 +1,122 @@ +import { ConfigService } from "@nestjs/config"; +import { TestingModule } from "@nestjs/testing"; + +import { RESPONSE_MOCK } from "test/utils/mock"; +import createTestingModule from "test/utils/mongo/createTestModule"; +import { + TOKEN_STUB, + TOKEN_USER_STUB, + USER_ID_PASSWORD_STUB, + USER_STUB_NON_PASSWORD, +} from "test/utils/stub"; + +import { AuthController } from "@/routes/auth/auth.controller"; +import { AuthService } from "@/routes/auth/auth.service"; +import { UserService } from "@/routes/user/user.service"; +import { EJwtTokenType } from "@/types"; + +jest.mock("@/routes/auth/auth.service"); +jest.mock("@/routes/user/user.service"); + +describe("AuthController", () => { + let controller: AuthController; + let authService: AuthService; + let userService: UserService; + + beforeEach(async () => { + const module: TestingModule = await createTestingModule({ + controllers: [AuthController], + providers: [AuthService, UserService, ConfigService], + }); + + controller = module.get(AuthController); + authService = module.get(AuthService); + userService = module.get(UserService); + }); + + describe("로그인", () => { + let userServiceGetByIdSpy: jest.SpyInstance; + let authServiceSigninSpy: jest.SpyInstance; + let authServiceRegisterTokenInCookieSpy: jest.SpyInstance; + + beforeEach(() => { + userServiceGetByIdSpy = jest.spyOn(userService, "getById"); + authServiceSigninSpy = jest.spyOn(authService, "signin"); + authServiceRegisterTokenInCookieSpy = jest.spyOn(authService, "registerTokenInCookie"); + }); + + it("성공", async () => { + userServiceGetByIdSpy.mockResolvedValueOnce(USER_STUB_NON_PASSWORD); + authServiceSigninSpy.mockResolvedValueOnce([TOKEN_STUB, TOKEN_STUB]); + + const result = await controller.signin(USER_ID_PASSWORD_STUB, TOKEN_USER_STUB, RESPONSE_MOCK); + + expect(userServiceGetByIdSpy).toBeCalledWith(TOKEN_USER_STUB._id); + expect(authServiceSigninSpy).toBeCalledWith(USER_STUB_NON_PASSWORD); + expect(authServiceRegisterTokenInCookieSpy).toBeCalledTimes(2); + expect(authServiceRegisterTokenInCookieSpy).toBeCalledWith({ + type: EJwtTokenType.ACCESS, + token: TOKEN_STUB, + res: RESPONSE_MOCK, + }); + expect(authServiceRegisterTokenInCookieSpy).toBeCalledWith({ + type: EJwtTokenType.REFRESH, + token: TOKEN_STUB, + res: RESPONSE_MOCK, + }); + expect(result).toEqual({ username: USER_STUB_NON_PASSWORD.username }); + }); + }); + + describe("로그아웃", () => { + let userServiceUpdateRefreshTokenSpy: jest.SpyInstance; + let authServiceClearCookieSpy: jest.SpyInstance; + + beforeEach(() => { + userServiceUpdateRefreshTokenSpy = jest.spyOn(userService, "updateRefreshToken"); + authServiceClearCookieSpy = jest.spyOn(authService, "clearCookie"); + }); + + it("성공", async () => { + await controller.signout(TOKEN_USER_STUB, RESPONSE_MOCK); + + expect(userServiceUpdateRefreshTokenSpy).toBeCalledTimes(1); + expect(userServiceUpdateRefreshTokenSpy).toBeCalledWith(TOKEN_USER_STUB._id, null); + expect(authServiceClearCookieSpy).toBeCalledTimes(1); + expect(authServiceClearCookieSpy).toBeCalledWith(RESPONSE_MOCK); + }); + }); + + describe("토큰 재발급", () => { + let authServiceSigninSpy: jest.SpyInstance; + let authServiceRegisterTokenInCookieSpy: jest.SpyInstance; + let authServiceVerifyRefreshTokenSpy: jest.SpyInstance; + let userServiceGetById: jest.SpyInstance; + + beforeEach(() => { + authServiceSigninSpy = jest.spyOn(authService, "signin"); + authServiceRegisterTokenInCookieSpy = jest.spyOn(authService, "registerTokenInCookie"); + authServiceVerifyRefreshTokenSpy = jest.spyOn(authService, "verifyRefreshToken"); + userServiceGetById = jest.spyOn(userService, "getById"); + }); + + it("성공", async () => { + authServiceSigninSpy.mockReturnValueOnce([TOKEN_STUB, TOKEN_STUB]); + authServiceVerifyRefreshTokenSpy.mockReturnValueOnce(true); + userServiceGetById.mockResolvedValueOnce(USER_STUB_NON_PASSWORD); + + await controller.refresh(TOKEN_USER_STUB, RESPONSE_MOCK); + + expect(authServiceSigninSpy).toBeCalledTimes(1); + expect(authServiceSigninSpy).toBeCalledWith(TOKEN_USER_STUB); + expect(authServiceVerifyRefreshTokenSpy).toBeCalledTimes(1); + expect(authServiceVerifyRefreshTokenSpy).toBeCalledWith(USER_STUB_NON_PASSWORD.refreshToken); + expect(authServiceRegisterTokenInCookieSpy).toBeCalledTimes(1); + expect(authServiceRegisterTokenInCookieSpy).toBeCalledWith({ + type: EJwtTokenType.ACCESS, + token: TOKEN_STUB, + res: RESPONSE_MOCK, + }); + }); + }); +}); diff --git a/test/routes/auth/auth.service.spec.ts b/test/routes/auth/auth.service.spec.ts new file mode 100644 index 0000000..a2bfe72 --- /dev/null +++ b/test/routes/auth/auth.service.spec.ts @@ -0,0 +1,139 @@ +import { JwtModule, JwtService } from "@nestjs/jwt"; +import { TestingModule } from "@nestjs/testing"; + +import { RESPONSE_MOCK } from "test/utils/mock"; +import createTestingModule from "test/utils/mongo/createTestModule"; +import { TOKEN_STUB, TOKEN_USER_STUB, USER_STUB } from "test/utils/stub"; + +import { AuthConstantProvider } from "@/common/providers/auth-constant.provider"; +import { AuthService } from "@/routes/auth/auth.service"; +import { EJwtTokenType } from "@/types"; +import { AUTH_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/user/user.service"); +jest.mock("@/common/providers/auth-constant.provider"); + +describe("AuthService", () => { + let authService: AuthService; + let jwtService: JwtService; + let authConstantProvider: AuthConstantProvider; + + beforeEach(async () => { + const module: TestingModule = await createTestingModule({ + imports: [JwtModule], + providers: [ + AuthService, + { + provide: AuthConstantProvider, + useValue: { + JWT_SECRET_KEY: "JWT_SECRET_KEY", + ACCESS_HEADER: "ACCESS_HEADER", + REFRESH_HEADER: "REFRESH_HEADER", + ACCESS_EXPIRES: "ACCESS_EXPIRES", + REFRESH_EXPIRES: "REFRESH_EXPIRES", + COOKIE_MAX_AGE: "COOKIE_MAX_AGE", + }, + }, + ], + }); + + authService = module.get(AuthService); + jwtService = module.get(JwtService); + authConstantProvider = module.get(AuthConstantProvider); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(authService).toBeDefined(); + expect(jwtService).toBeDefined(); + expect(authConstantProvider).toBeDefined(); + }); + + describe("로그인", () => { + it("성공", async () => { + authService.signature = jest.fn().mockReturnValue(TOKEN_STUB); + + const result = await authService.signin(TOKEN_USER_STUB); + + expect(authService.signature).toHaveBeenCalledTimes(2); + expect(authService.signature).toReturnWith(TOKEN_STUB); + expect(result).toEqual([TOKEN_STUB, TOKEN_STUB]); + }); + }); + + describe("refresh token 유효성 검사", () => { + let jwtServiceVerifySpy: jest.SpyInstance; + + beforeEach(() => { + jwtServiceVerifySpy = jest.spyOn(jwtService, "verify"); + }); + + it("성공", async () => { + jwtServiceVerifySpy.mockReturnValueOnce(true); + + const result = await authService.verifyRefreshToken(USER_STUB.refreshToken); + + expect(jwtServiceVerifySpy).toHaveBeenCalledTimes(1); + expect(result).toBe(true); + }); + + describe("실패", () => { + it("유저 정보에 refresh token이 없는 경우", async () => { + try { + await authService.verifyRefreshToken(null); + } catch (e) { + expect(e.status).toBe(401); + expect(jwtServiceVerifySpy).toHaveBeenCalledTimes(1); + expect(e.message).toBe(AUTH_ERROR.UNAUTHORIZED); + } + }); + + it("토큰이 유효하지 않은 경우", async () => { + jwtServiceVerifySpy.mockRejectedValueOnce(new Error()); + + try { + await authService.verifyRefreshToken(USER_STUB.refreshToken); + } catch (e) { + expect(e.status).toBe(401); + expect(jwtServiceVerifySpy).toHaveBeenCalledTimes(1); + expect(e.message).toBe(AUTH_ERROR.UNAUTHORIZED); + } + }); + }); + }); + + describe("response header에 토큰 등록", () => { + const res = RESPONSE_MOCK; + + it("access token", () => { + authService.registerTokenInCookie({ + type: EJwtTokenType.ACCESS, + token: TOKEN_STUB, + res, + }); + + expect(res.cookie).toHaveBeenCalledTimes(1); + expect(res.cookie).toHaveBeenCalledWith(authConstantProvider.ACCESS_HEADER, TOKEN_STUB, { + httpOnly: false, + maxAge: Number(authConstantProvider.COOKIE_MAX_AGE), + secure: true, + }); + }); + + it("refresh token", () => { + authService.registerTokenInCookie({ + type: EJwtTokenType.REFRESH, + token: TOKEN_STUB, + res, + }); + + expect(res.cookie).toHaveBeenCalledTimes(1); + expect(res.cookie).toHaveBeenCalledWith(authConstantProvider.REFRESH_HEADER, TOKEN_STUB, { + httpOnly: true, + maxAge: Number(authConstantProvider.COOKIE_MAX_AGE), + secure: true, + }); + }); + }); +}); diff --git a/test/routes/experience/experience.controller.spec.ts b/test/routes/experience/experience.controller.spec.ts new file mode 100644 index 0000000..5409a6c --- /dev/null +++ b/test/routes/experience/experience.controller.spec.ts @@ -0,0 +1,84 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { CREATE_EXPERIENCE_STUB, EXPERIENCE_STUB } from "test/utils/stub"; + +import { ExperienceController } from "@/routes/experience/experience.controller"; +import { ExperienceService } from "@/routes/experience/experience.service"; + +jest.mock("@/routes/experience/experience.service"); + +describe("ExperienceController", () => { + let controller: ExperienceController; + let service: ExperienceService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ExperienceController], + providers: [ExperienceService], + }).compile(); + + controller = module.get(ExperienceController); + service = module.get(ExperienceService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + expect(service).toBeDefined(); + }); + + describe("경력 생성", () => { + it("성공", async () => { + const serviceCreateSpy = jest.spyOn(service, "create").mockResolvedValueOnce(EXPERIENCE_STUB); + + const result = await controller.create(CREATE_EXPERIENCE_STUB); + + expect(result).toEqual(EXPERIENCE_STUB); + + expect(serviceCreateSpy).toBeCalledTimes(1); + expect(serviceCreateSpy).toBeCalledWith(CREATE_EXPERIENCE_STUB); + }); + }); + + describe("경력 수정", () => { + it("성공", async () => { + const serviceUpdateSpy = jest.spyOn(service, "update").mockResolvedValueOnce(EXPERIENCE_STUB); + + const result = await controller.update(EXPERIENCE_STUB._id, CREATE_EXPERIENCE_STUB); + + expect(result).toEqual(EXPERIENCE_STUB); + + expect(serviceUpdateSpy).toBeCalledTimes(1); + expect(serviceUpdateSpy).toBeCalledWith(EXPERIENCE_STUB._id, CREATE_EXPERIENCE_STUB); + }); + }); + + describe("경력 삭제", () => { + it("성공", async () => { + const serviceDeleteSpy = jest.spyOn(service, "delete").mockResolvedValueOnce(); + + const result = await controller.delete(EXPERIENCE_STUB._id); + + expect(result).toEqual(true); + + expect(serviceDeleteSpy).toBeCalledTimes(1); + expect(serviceDeleteSpy).toBeCalledWith(EXPERIENCE_STUB._id); + }); + }); + + describe("경력 조회", () => { + it("성공", async () => { + const serviceGetAllSpy = jest + .spyOn(service, "getAll") + .mockResolvedValueOnce([EXPERIENCE_STUB]); + + const result = await controller.getAll(); + + expect(result).toEqual([EXPERIENCE_STUB]); + + expect(serviceGetAllSpy).toBeCalledTimes(1); + expect(serviceGetAllSpy).toBeCalledWith(); + }); + }); +}); diff --git a/test/routes/experience/experience.service.spec.ts b/test/routes/experience/experience.service.spec.ts new file mode 100644 index 0000000..aea4ccc --- /dev/null +++ b/test/routes/experience/experience.service.spec.ts @@ -0,0 +1,191 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { CREATE_EXPERIENCE_STUB, EXPERIENCE_STUB } from "test/utils/stub"; + +import { ExperienceRepository } from "@/routes/experience/experience.repository"; +import { ExperienceService } from "@/routes/experience/experience.service"; +import { EXPERIENCE_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/experience/experience.repository"); + +describe("ExperienceService", () => { + let service: ExperienceService; + let repository: ExperienceRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ExperienceService, ExperienceRepository], + }).compile(); + + service = module.get(ExperienceService); + repository = module.get(ExperienceRepository); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + expect(repository).toBeDefined(); + }); + + describe("경력 생성", () => { + let repositoryCreateSpy: jest.SpyInstance; + let repositoryGetOneSpy: jest.SpyInstance; + + const GET_ALL_PARAMS = { title: CREATE_EXPERIENCE_STUB.title }; + + beforeEach(() => { + repositoryCreateSpy = jest.spyOn(repository, "create"); + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + repositoryCreateSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + + const result = await service.create(CREATE_EXPERIENCE_STUB); + + expect(result).toEqual(EXPERIENCE_STUB); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(repositoryCreateSpy).toBeCalledTimes(1); + expect(repositoryCreateSpy).toBeCalledWith(CREATE_EXPERIENCE_STUB); + }); + + it("실패 - 이미 존재하는 경력", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + + await expect(service.create(CREATE_EXPERIENCE_STUB)).rejects.toThrowError( + EXPERIENCE_ERROR.ALREADY_EXISTS, + ); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(repositoryCreateSpy).toBeCalledTimes(0); + }); + }); + + describe("경력 수정", () => { + let repositoryGetByIdSpy: jest.SpyInstance; + let repositoryGetOneSpy: jest.SpyInstance; + let repositoryFindOneAndUpdateSpy: jest.SpyInstance; + + const GET_ALL_PARAMS = { title: EXPERIENCE_STUB.title, _id: { $ne: EXPERIENCE_STUB._id } }; + + beforeEach(() => { + repositoryGetByIdSpy = jest.spyOn(repository, "getById"); + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + repositoryFindOneAndUpdateSpy = jest.spyOn(repository, "findOneAndUpdate"); + }); + + it("성공", async () => { + repositoryGetByIdSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + repositoryGetOneSpy.mockResolvedValueOnce(null); + repositoryFindOneAndUpdateSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + + const result = await service.update(EXPERIENCE_STUB._id, CREATE_EXPERIENCE_STUB); + + expect(result).toEqual(EXPERIENCE_STUB); + + expect(repositoryGetByIdSpy).toBeCalledTimes(1); + expect(repositoryGetByIdSpy).toBeCalledWith(EXPERIENCE_STUB._id); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(repositoryFindOneAndUpdateSpy).toBeCalledTimes(1); + expect(repositoryFindOneAndUpdateSpy).toBeCalledWith( + { _id: EXPERIENCE_STUB._id }, + CREATE_EXPERIENCE_STUB, + ); + }); + + it("실패 - 존재하지 않는 경력", async () => { + repositoryGetByIdSpy.mockResolvedValueOnce(null); + + await expect( + service.update(EXPERIENCE_STUB._id, CREATE_EXPERIENCE_STUB), + ).rejects.toThrowError(EXPERIENCE_ERROR.NOT_FOUND); + + expect(repositoryGetByIdSpy).toBeCalledTimes(1); + expect(repositoryGetByIdSpy).toBeCalledWith(EXPERIENCE_STUB._id); + + expect(repositoryGetOneSpy).toBeCalledTimes(0); + + expect(repositoryFindOneAndUpdateSpy).toBeCalledTimes(0); + }); + + it("실패 - 이미 존재하는 경력", async () => { + repositoryGetByIdSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + repositoryGetOneSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + + await expect( + service.update(EXPERIENCE_STUB._id, CREATE_EXPERIENCE_STUB), + ).rejects.toThrowError(EXPERIENCE_ERROR.ALREADY_EXISTS); + + expect(repositoryGetByIdSpy).toBeCalledTimes(1); + expect(repositoryGetByIdSpy).toBeCalledWith(EXPERIENCE_STUB._id); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(repositoryFindOneAndUpdateSpy).toBeCalledTimes(0); + }); + }); + + describe("경력 삭제", () => { + let repositoryGetByIdSpy: jest.SpyInstance; + let repositoryDeleteSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetByIdSpy = jest.spyOn(repository, "getById"); + repositoryDeleteSpy = jest.spyOn(repository, "delete"); + }); + + it("성공", async () => { + repositoryGetByIdSpy.mockResolvedValueOnce(EXPERIENCE_STUB); + repositoryDeleteSpy.mockResolvedValueOnce(true); + + const result = await service.delete(EXPERIENCE_STUB._id); + + expect(result).toEqual(true); + + expect(repositoryGetByIdSpy).toBeCalledTimes(1); + expect(repositoryGetByIdSpy).toBeCalledWith(EXPERIENCE_STUB._id); + + expect(repositoryDeleteSpy).toBeCalledTimes(1); + expect(repositoryDeleteSpy).toBeCalledWith(EXPERIENCE_STUB._id); + }); + + it("실패 - 존재하지 않는 경력", async () => { + repositoryGetByIdSpy.mockResolvedValueOnce(null); + + await expect(service.delete(EXPERIENCE_STUB._id)).rejects.toThrowError( + EXPERIENCE_ERROR.NOT_FOUND, + ); + + expect(repositoryGetByIdSpy).toBeCalledTimes(1); + expect(repositoryGetByIdSpy).toBeCalledWith(EXPERIENCE_STUB._id); + + expect(repositoryDeleteSpy).toBeCalledTimes(0); + }); + }); + + describe("모든 경력 조회", () => { + it("성공", async () => { + const repositoryGetAllSpy = jest.spyOn(repository, "getAll"); + + repositoryGetAllSpy.mockResolvedValueOnce([EXPERIENCE_STUB]); + + const result = await service.getAll(); + + expect(result).toEqual([EXPERIENCE_STUB]); + + expect(repositoryGetAllSpy).toBeCalledTimes(1); + expect(repositoryGetAllSpy).toBeCalledWith({}, {}, { sort: { start: -1 } }); + }); + }); +}); diff --git a/test/routes/post/post.controller.spec.ts b/test/routes/post/post.controller.spec.ts new file mode 100644 index 0000000..e94ccf6 --- /dev/null +++ b/test/routes/post/post.controller.spec.ts @@ -0,0 +1,156 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { GET_POSTS_DTO_STUB, POST_CREATE_STUB, POST_STUB, POST_UPDATE_STUB } from "test/utils/stub"; + +import { PostController } from "@/routes/post/post.controller"; +import { PostService } from "@/routes/post/post.service"; + +jest.mock("@/routes/post/post.service"); + +describe("PostController", () => { + let controller: PostController; + let service: PostService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [PostController], + providers: [PostService], + }).compile(); + + controller = module.get(PostController); + service = module.get(PostService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + expect(service).toBeDefined(); + }); + + describe("게시글 생성", () => { + it("성공", async () => { + const serviceCreateSpy: jest.SpyInstance = jest.spyOn(service, "create"); + serviceCreateSpy.mockResolvedValueOnce(POST_STUB); + + const result = await controller.create(POST_CREATE_STUB); + + expect(result).toEqual(POST_STUB); + expect(serviceCreateSpy).toBeCalledTimes(1); + expect(serviceCreateSpy).toBeCalledWith(POST_CREATE_STUB); + }); + }); + + describe("게시글 수정", () => { + it("성공", async () => { + const serviceUpdateSpy: jest.SpyInstance = jest.spyOn(service, "update"); + serviceUpdateSpy.mockResolvedValueOnce(POST_STUB); + + const nid = POST_STUB.nid; + const result = await controller.update(nid, POST_UPDATE_STUB); + + expect(result).toEqual(POST_STUB); + expect(serviceUpdateSpy).toBeCalledTimes(1); + expect(serviceUpdateSpy).toBeCalledWith(nid, POST_UPDATE_STUB); + }); + }); + + describe("게시글 삭제", () => { + it("성공", async () => { + const serviceDeleteSpy: jest.SpyInstance = jest.spyOn(service, "delete"); + serviceDeleteSpy.mockResolvedValueOnce(undefined); + + const nid = POST_STUB.nid; + + const result = await controller.delete(nid); + + expect(result).toBeUndefined(); + expect(serviceDeleteSpy).toBeCalledTimes(1); + expect(serviceDeleteSpy).toBeCalledWith(nid); + }); + }); + + describe("게시글 조회", () => { + it("전체 조회", async () => { + const serviceGetAllSpy: jest.SpyInstance = jest.spyOn(service, "getAll"); + serviceGetAllSpy.mockResolvedValueOnce([POST_STUB]); + + const result = await controller.getPosts(GET_POSTS_DTO_STUB); + + expect(result).toEqual([POST_STUB]); + expect(serviceGetAllSpy).toBeCalledTimes(1); + expect(serviceGetAllSpy).toBeCalledWith(GET_POSTS_DTO_STUB); + }); + + it("number id를 통한 조회", async () => { + const nid = POST_STUB.nid; + + const POST_TO_RESULT = { + ...POST_STUB, + likeCount: 0, + viewCount: 0, + isLiked: false, + }; + + const serviceGetByNumIdSpy: jest.SpyInstance = jest.spyOn(service, "getByNumId"); + const serviceIncreaseViewCountSpy: jest.SpyInstance = jest.spyOn(service, "increaseToViews"); + + serviceGetByNumIdSpy.mockResolvedValueOnce(POST_TO_RESULT); + serviceIncreaseViewCountSpy.mockResolvedValueOnce(undefined); + + const result = await controller.getPost(nid, "ip"); + + expect(result).toEqual(POST_TO_RESULT); + + expect(serviceGetByNumIdSpy).toBeCalledTimes(1); + expect(serviceGetByNumIdSpy).toBeCalledWith(nid, "ip"); + + expect(serviceIncreaseViewCountSpy).toBeCalledTimes(1); + expect(serviceIncreaseViewCountSpy).toBeCalledWith(nid, "ip"); + }); + + it("이전/다음 게시글 조회", async () => { + const POST_TO_RESULT = { + prev: POST_STUB, + next: POST_STUB, + }; + + const serviceGetSiblingSpy: jest.SpyInstance = jest.spyOn(service, "getSibling"); + serviceGetSiblingSpy.mockResolvedValueOnce(POST_TO_RESULT); + + const result = await controller.getSiblingPost(POST_STUB.nid); + + expect(result).toEqual(POST_TO_RESULT); + expect(serviceGetSiblingSpy).toBeCalledTimes(1); + expect(serviceGetSiblingSpy).toBeCalledWith(POST_STUB.nid); + }); + }); + + describe("게시글 좋아요", () => { + it("증가", async () => { + const serviceIncreaseLikeCountSpy: jest.SpyInstance = jest.spyOn(service, "increaseToLikes"); + serviceIncreaseLikeCountSpy.mockResolvedValueOnce(undefined); + + const nid = POST_STUB.nid; + + const result = await controller.like(nid, "ip"); + + expect(result).toBeUndefined(); + expect(serviceIncreaseLikeCountSpy).toBeCalledTimes(1); + expect(serviceIncreaseLikeCountSpy).toBeCalledWith(nid, "ip"); + }); + + it("감소", async () => { + const serviceDecreaseLikeCountSpy: jest.SpyInstance = jest.spyOn(service, "decreaseToLikes"); + serviceDecreaseLikeCountSpy.mockResolvedValueOnce(undefined); + + const nid = POST_STUB.nid; + + const result = await controller.unlike(nid, "ip"); + + expect(result).toBeUndefined(); + expect(serviceDecreaseLikeCountSpy).toBeCalledTimes(1); + expect(serviceDecreaseLikeCountSpy).toBeCalledWith(nid, "ip"); + }); + }); +}); diff --git a/test/routes/post/post.service.spec.ts b/test/routes/post/post.service.spec.ts new file mode 100644 index 0000000..393bb37 --- /dev/null +++ b/test/routes/post/post.service.spec.ts @@ -0,0 +1,492 @@ +import { BadRequestException } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; + +import { + POST_CREATE_STUB, + POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES, + POST_STUB, + POST_UPDATE_STUB, + SERIES_STUB, + TAG_STUB, +} from "test/utils/stub"; + +import { PostRepository } from "@/routes/post/post.repository"; +import { PostService } from "@/routes/post/post.service"; +import { SeriesService } from "@/routes/series/series.service"; +import { TagService } from "@/routes/tag/tag.service"; +import { POST_ERROR, POST_FIND_PROJECTION } from "@/utils/constants"; +import { filterQueryByPosts } from "@/utils/filterQueryByPosts"; + +jest.mock("@/routes/post/post.repository"); +jest.mock("@/routes/series/series.service"); +jest.mock("@/routes/tag/tag.service"); +jest.mock("@/common/decorators/transaction.decorator", () => ({ + Transactional: () => { + return jest.fn(); + }, +})); + +describe("PostService", () => { + let postRepository: PostRepository; + let postService: PostService; + let seriesService: SeriesService; + let tagService: TagService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [PostService, PostRepository, SeriesService, TagService], + }).compile(); + + postRepository = module.get(PostRepository); + postService = module.get(PostService); + seriesService = module.get(SeriesService); + tagService = module.get(TagService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(postRepository).toBeDefined(); + expect(postService).toBeDefined(); + expect(seriesService).toBeDefined(); + expect(tagService).toBeDefined(); + }); + + describe("게시글 생성", () => { + let postRepositoryCreateSpy: jest.SpyInstance; + let seriesServicePushPostSpy: jest.SpyInstance; + let tagServicePushPostIdInTagsSpy: jest.SpyInstance; + + let POST; + + beforeEach(() => { + POST = { + ...POST_STUB, + save: jest.fn().mockResolvedValue(POST_STUB), + populate: jest.fn().mockResolvedValue(POST_STUB), + }; + + postRepositoryCreateSpy = jest.spyOn(postRepository, "create"); + seriesServicePushPostSpy = jest.spyOn(seriesService, "pushPostIdInSeries"); + tagServicePushPostIdInTagsSpy = jest.spyOn(tagService, "pushPostIdInTags"); + }); + + it("성공", async () => { + postRepositoryCreateSpy.mockResolvedValueOnce(POST); + seriesServicePushPostSpy.mockResolvedValueOnce(SERIES_STUB); + tagServicePushPostIdInTagsSpy.mockResolvedValueOnce([TAG_STUB]); + + const { tags, series, ...rest } = POST_CREATE_STUB; + + const post = await postService.create(POST_CREATE_STUB); + + expect(post).toEqual(POST); + expect(postRepositoryCreateSpy).toBeCalledTimes(1); + expect(postRepositoryCreateSpy).toBeCalledWith(rest); + expect(seriesServicePushPostSpy).toBeCalledTimes(1); + expect(seriesServicePushPostSpy).toBeCalledWith(series, POST._id); + expect(tagServicePushPostIdInTagsSpy).toBeCalledTimes(1); + expect(tagServicePushPostIdInTagsSpy).toBeCalledWith(tags, POST._id); + }); + + it("성공 - 태그, 시리즈 없을 때", async () => { + postRepositoryCreateSpy.mockResolvedValueOnce(POST); + + const post = await postService.create(POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES); + + expect(post).toEqual(POST); + expect(postRepositoryCreateSpy).toBeCalledTimes(1); + expect(postRepositoryCreateSpy).toBeCalledWith(POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES); + expect(seriesServicePushPostSpy).toBeCalledTimes(0); + expect(tagServicePushPostIdInTagsSpy).toBeCalledTimes(0); + }); + + it("실패 - 이미 존재하는 게시글", async () => { + postRepositoryCreateSpy.mockRejectedValueOnce( + new BadRequestException(POST_ERROR.FAIL_CREATE), + ); + + const { tags: _, series: __, ...rest } = POST_CREATE_STUB; + + try { + await postService.create(POST_CREATE_STUB); + } catch (e) { + expect(e).toBeInstanceOf(BadRequestException); + expect(e.message).toEqual(POST_ERROR.FAIL_CREATE); + + expect(postRepositoryCreateSpy).toBeCalledTimes(1); + expect(postRepositoryCreateSpy).toBeCalledWith(rest); + + expect(seriesServicePushPostSpy).toBeCalledTimes(0); + expect(tagServicePushPostIdInTagsSpy).toBeCalledTimes(0); + } + }); + }); + + describe("게시글 수정", () => { + const postId = POST_STUB._id; + const nid = POST_STUB.nid; + + let postRepositoryGetOneSpy: jest.SpyInstance; + let postRepositoryUpdateSpy: jest.SpyInstance; + let postRepositoryPushTagsSpy: jest.SpyInstance; + let postRepositoryPullTagsSpy: jest.SpyInstance; + let tagServicePushPostIdInTagsSpy: jest.SpyInstance; + let tagServicePullPostIdInTagsSpy: jest.SpyInstance; + let seriesServicePushPostIdSpy: jest.SpyInstance; + let seriesServicePullPostIdSpy: jest.SpyInstance; + + beforeEach(() => { + postRepositoryGetOneSpy = jest.spyOn(postRepository, "getOne"); + postRepositoryUpdateSpy = jest.spyOn(postRepository, "update"); + postRepositoryPushTagsSpy = jest.spyOn(postRepository, "pushTags"); + postRepositoryPullTagsSpy = jest.spyOn(postRepository, "pullTags"); + tagServicePushPostIdInTagsSpy = jest.spyOn(tagService, "pushPostIdInTags"); + tagServicePullPostIdInTagsSpy = jest.spyOn(tagService, "pullPostIdInTags"); + seriesServicePushPostIdSpy = jest.spyOn(seriesService, "pushPostIdInSeries"); + seriesServicePullPostIdSpy = jest.spyOn(seriesService, "pullPostIdInSeries"); + }); + + it("성공", async () => { + const POST = { ...POST_STUB }; + + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + postRepositoryUpdateSpy.mockResolvedValueOnce(POST); + postRepositoryPushTagsSpy.mockResolvedValueOnce(undefined); + postRepositoryPullTagsSpy.mockResolvedValueOnce(undefined); + tagServicePushPostIdInTagsSpy.mockResolvedValueOnce([TAG_STUB]); + tagServicePullPostIdInTagsSpy.mockResolvedValueOnce([TAG_STUB]); + seriesServicePushPostIdSpy.mockResolvedValueOnce(SERIES_STUB); + seriesServicePullPostIdSpy.mockResolvedValueOnce(undefined); + + const { deleteTags, addTags, ...rest } = POST_UPDATE_STUB; + + const post = await postService.update(nid, { ...POST_UPDATE_STUB }); + + expect(post).toEqual(POST); + expect(postRepositoryGetOneSpy).toBeCalledTimes(2); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + + expect(postRepositoryUpdateSpy).toBeCalledTimes(1); + expect(postRepositoryUpdateSpy).toBeCalledWith(postId, { ...rest, series: SERIES_STUB._id }); + + expect(postRepositoryPushTagsSpy).toBeCalledTimes(1); + expect(postRepositoryPushTagsSpy).toBeCalledWith(postId, [TAG_STUB]); + + expect(postRepositoryPullTagsSpy).toBeCalledTimes(1); + expect(postRepositoryPullTagsSpy).toBeCalledWith(postId, [TAG_STUB]); + + expect(tagServicePushPostIdInTagsSpy).toBeCalledTimes(1); + expect(tagServicePushPostIdInTagsSpy).toBeCalledWith(addTags, postId); + + expect(tagServicePullPostIdInTagsSpy).toBeCalledTimes(1); + expect(tagServicePullPostIdInTagsSpy).toBeCalledWith(deleteTags, postId); + + expect(seriesServicePushPostIdSpy).toBeCalledTimes(1); + expect(seriesServicePushPostIdSpy).toBeCalledWith(POST_UPDATE_STUB.series, postId); + + expect(seriesServicePullPostIdSpy).toBeCalledTimes(1); + expect(seriesServicePullPostIdSpy).toBeCalledWith(POST_STUB.series.name, postId); + }); + + it("실패 - 존재하지 않는 게시글", async () => { + postRepositoryGetOneSpy.mockResolvedValueOnce(null); + + await expect(postService.update(nid, { ...POST_UPDATE_STUB })).rejects.toThrowError(); + + expect(postRepositoryGetOneSpy).toBeCalledTimes(1); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + expect(postRepositoryUpdateSpy).toBeCalledTimes(0); + expect(postRepositoryPushTagsSpy).toBeCalledTimes(0); + expect(postRepositoryPullTagsSpy).toBeCalledTimes(0); + expect(tagServicePushPostIdInTagsSpy).toBeCalledTimes(0); + expect(tagServicePullPostIdInTagsSpy).toBeCalledTimes(0); + expect(seriesServicePushPostIdSpy).toBeCalledTimes(0); + expect(seriesServicePullPostIdSpy).toBeCalledTimes(0); + }); + + it("실패 - 업데이트시 에러 발생", async () => { + const POST = { ...POST_STUB }; + + POST.series = null; + + const POST_UPDATE = { ...POST_UPDATE_STUB }; + POST_UPDATE.series = null; + + const { deleteTags: _, addTags: __, ...rest } = POST_UPDATE; + + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + postRepositoryUpdateSpy.mockRejectedValueOnce(new Error(POST_ERROR.FAIL_UPDATE)); + + try { + await postService.update(nid, POST_UPDATE); + } catch (e) { + expect(e).toBeInstanceOf(BadRequestException); + expect(e.status).toEqual(400); + expect(e.message).toEqual(POST_ERROR.FAIL_UPDATE); + + expect(postRepositoryGetOneSpy).toBeCalledTimes(1); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + + expect(postRepositoryUpdateSpy).toBeCalledTimes(1); + expect(postRepositoryUpdateSpy).toBeCalledWith(postId, rest); + + expect(postRepositoryPushTagsSpy).toBeCalledTimes(0); + expect(postRepositoryPullTagsSpy).toBeCalledTimes(0); + expect(tagServicePushPostIdInTagsSpy).toBeCalledTimes(0); + expect(tagServicePullPostIdInTagsSpy).toBeCalledTimes(0); + expect(seriesServicePushPostIdSpy).toBeCalledTimes(0); + expect(seriesServicePullPostIdSpy).toBeCalledTimes(0); + } + }); + }); + + describe("게시글 삭제", () => { + const postId = POST_STUB._id; + const nid = POST_STUB.nid; + + let postRepositoryGetOneSpy: jest.SpyInstance; + let postRepositoryDeleteSpy: jest.SpyInstance; + let seriesServicePullPostIdSpy: jest.SpyInstance; + let tagServicePullPostIdInTagsSpy: jest.SpyInstance; + + beforeEach(() => { + postRepositoryGetOneSpy = jest.spyOn(postRepository, "getOne"); + postRepositoryDeleteSpy = jest.spyOn(postRepository, "delete"); + seriesServicePullPostIdSpy = jest.spyOn(seriesService, "pullPostIdInSeries"); + tagServicePullPostIdInTagsSpy = jest.spyOn(tagService, "pullPostIdInTags"); + }); + + it("성공", async () => { + const POST = { ...POST_STUB }; + + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + postRepositoryDeleteSpy.mockResolvedValueOnce(undefined); + seriesServicePullPostIdSpy.mockResolvedValueOnce(undefined); + tagServicePullPostIdInTagsSpy.mockResolvedValueOnce(undefined); + + await postService.delete(nid); + + expect(postRepositoryGetOneSpy).toBeCalledTimes(1); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }, {}, { populate: ["tags", "series"] }); + + expect(postRepositoryDeleteSpy).toBeCalledTimes(1); + expect(postRepositoryDeleteSpy).toBeCalledWith(postId); + + expect(seriesServicePullPostIdSpy).toBeCalledTimes(1); + expect(seriesServicePullPostIdSpy).toBeCalledWith(POST.series.name, postId); + + expect(tagServicePullPostIdInTagsSpy).toBeCalledTimes(1); + expect(tagServicePullPostIdInTagsSpy).toBeCalledWith( + POST.tags.map((tag) => tag.name), + postId, + ); + }); + }); + + describe("게시글 좋아요", () => { + const nid = POST_STUB.nid; + + const POST = { ...POST_STUB }; + + const POST_TO_RESULT = { + ...POST, + likeCount: 0, + viewCount: 0, + isLike: false, + }; + + let postRepositoryGetOneSpy: jest.SpyInstance; + + beforeEach(() => { + postRepositoryGetOneSpy = jest.spyOn(postRepository, "getOne"); + }); + + it("증가", async () => { + const postRepositoryIncreaseLikeSpy: jest.SpyInstance = jest.spyOn( + postRepository, + "increaseToLikes", + ); + const postRepositoryGetOneSpy: jest.SpyInstance = jest.spyOn(postRepository, "getOne"); + + postRepositoryGetOneSpy.mockResolvedValueOnce(POST_TO_RESULT); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + postRepositoryIncreaseLikeSpy.mockResolvedValueOnce(undefined); + + const projection = { ...POST_FIND_PROJECTION, isLiked: { $in: ["ip", "$likes"] } }; + + await postService.increaseToLikes(nid, "ip"); + + expect(postRepositoryIncreaseLikeSpy).toBeCalledTimes(1); + expect(postRepositoryIncreaseLikeSpy).toBeCalledWith(nid, "ip"); + expect(postRepositoryGetOneSpy).toBeCalledTimes(2); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }, projection); + }); + + it("감소", async () => { + const postRepositoryDecreaseLikeSpy: jest.SpyInstance = jest.spyOn( + postRepository, + "decreaseToLikes", + ); + + postRepositoryDecreaseLikeSpy.mockResolvedValueOnce(undefined); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + + await postService.decreaseToLikes(nid, "ip"); + + expect(postRepositoryDecreaseLikeSpy).toBeCalledTimes(1); + expect(postRepositoryDecreaseLikeSpy).toBeCalledWith(nid, "ip"); + expect(postRepositoryGetOneSpy).toBeCalledTimes(1); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + }); + }); + + describe("게시글 조회수", () => { + const nid = POST_STUB.nid; + const POST = { ...POST_STUB }; + + it("증가", async () => { + const postRepositoryIncreaseViewsSpy: jest.SpyInstance = jest.spyOn( + postRepository, + "increaseToViews", + ); + const postRepositoryGetOneSpy: jest.SpyInstance = jest.spyOn(postRepository, "getOne"); + + postRepositoryIncreaseViewsSpy.mockResolvedValueOnce(undefined); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + + await postService.increaseToViews(nid, "ip"); + + expect(postRepositoryIncreaseViewsSpy).toBeCalledTimes(1); + expect(postRepositoryIncreaseViewsSpy).toBeCalledWith(nid, "ip"); + expect(postRepositoryGetOneSpy).toBeCalledTimes(1); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid }); + }); + }); + + describe("게시글 전체 조회", () => { + const POSTS = [POST_STUB]; + + const BASE_OPTIONS = { + sort: { _id: -1 }, + populate: ["tags", "series"], + limit: 10, + skip: 0, + }; + + let postRepositoryGetAllSpy: jest.SpyInstance; + + beforeEach(() => { + postRepositoryGetAllSpy = jest.spyOn(postRepository, "getAll"); + postRepositoryGetAllSpy.mockResolvedValueOnce(POSTS); + }); + + it("성공", async () => { + const posts = await postService.getAll({}); + + expect(posts).toEqual(POSTS); + expect(postRepositoryGetAllSpy).toBeCalledTimes(1); + expect(postRepositoryGetAllSpy).toBeCalledWith({}, POST_FIND_PROJECTION, BASE_OPTIONS); + }); + + it("성공 - 텍스트", async () => { + const dto = { + text: "text", + }; + const filter = filterQueryByPosts(dto); + + const posts = await postService.getAll(dto); + + expect(posts).toEqual(POSTS); + expect(postRepositoryGetAllSpy).toBeCalledTimes(1); + expect(postRepositoryGetAllSpy).toBeCalledWith(filter, POST_FIND_PROJECTION, BASE_OPTIONS); + }); + + it("성공 - 시리즈/태그", async () => { + const dto = { + series: POST_STUB.series.name, + tag: TAG_STUB.name, + }; + + const filter = filterQueryByPosts(dto); + + const posts = await postService.getAll(dto); + + expect(posts).toEqual(POSTS); + expect(postRepositoryGetAllSpy).toBeCalledTimes(1); + expect(postRepositoryGetAllSpy).toBeCalledWith(filter, POST_FIND_PROJECTION, BASE_OPTIONS); + }); + + it("성공 - 텍스트/시리즈/태그/스킵/리미트", async () => { + const dto = { + text: "text", + series: POST_STUB.series.name, + tag: TAG_STUB.name, + skip: 1000, + limit: 1000, + }; + + const filter = filterQueryByPosts(dto); + const options = { + ...BASE_OPTIONS, + skip: dto.skip, + limit: dto.limit, + }; + + const posts = await postService.getAll(dto); + + expect(posts).toEqual(POSTS); + expect(postRepositoryGetAllSpy).toBeCalledTimes(1); + expect(postRepositoryGetAllSpy).toBeCalledWith(filter, POST_FIND_PROJECTION, options); + }); + }); + + describe("게시글 조회", () => { + const POST = { ...POST_STUB }; + + it("성공 - 숫자 id로 조회", async () => { + const postRepositoryGetOneSpy: jest.SpyInstance = jest.spyOn(postRepository, "getOne"); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + + const projection = { ...POST_FIND_PROJECTION, isLiked: { $in: ["ip", "$likes"] } }; + const options = { populate: ["tags", "series"] }; + + const post = await postService.getByNumId(POST.nid, "ip"); + + expect(post).toEqual(POST); + expect(postRepositoryGetOneSpy).toBeCalledTimes(1); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid: POST.nid }, projection, options); + }); + + it("성공 - Object id로 조회", async () => { + const postRepositoryGetByIdSpy: jest.SpyInstance = jest.spyOn(postRepository, "getById"); + postRepositoryGetByIdSpy.mockResolvedValueOnce(POST); + + const post = await postService.getById(POST._id); + + expect(post).toEqual(POST); + expect(postRepositoryGetByIdSpy).toBeCalledTimes(1); + expect(postRepositoryGetByIdSpy).toBeCalledWith(POST._id); + }); + + it("성공 - 특정 게시글을 기준으로 이전/다음 게시글 조회", async () => { + const postRepositoryGetOneSpy: jest.SpyInstance = jest.spyOn(postRepository, "getOne"); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + postRepositoryGetOneSpy.mockResolvedValueOnce(POST); + + const projection = { _id: 1, nid: 1, title: 1 }; + + const post = await postService.getSibling(POST.nid); + + expect(post).toEqual({ prev: POST, next: POST }); + expect(postRepositoryGetOneSpy).toBeCalledTimes(2); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid: { $lt: POST.nid } }, projection, { + sort: { nid: -1 }, + }); + expect(postRepositoryGetOneSpy).toBeCalledWith({ nid: { $gt: POST.nid } }, projection); + }); + }); +}); diff --git a/test/routes/project/project.controller.spec.ts b/test/routes/project/project.controller.spec.ts new file mode 100644 index 0000000..aa5a449 --- /dev/null +++ b/test/routes/project/project.controller.spec.ts @@ -0,0 +1,95 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { PROJECT_STUB } from "test/utils/stub"; + +import { ProjectController } from "@/routes/project/project.controller"; +import { ProjectService } from "@/routes/project/project.service"; + +jest.mock("@/routes/project/project.service"); + +describe("ProjectController", () => { + let controller: ProjectController; + let service: ProjectService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ProjectController], + providers: [ProjectService], + }).compile(); + + controller = module.get(ProjectController); + service = module.get(ProjectService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + expect(service).toBeDefined(); + }); + + describe("프로젝트 생성", () => { + it("성공", async () => { + const serviceCreateSpy: jest.SpyInstance = jest.spyOn(service, "create"); + serviceCreateSpy.mockResolvedValueOnce(PROJECT_STUB); + + const result = await controller.create(PROJECT_STUB); + + expect(result).toEqual(PROJECT_STUB); + expect(serviceCreateSpy).toBeCalledTimes(1); + expect(serviceCreateSpy).toBeCalledWith(PROJECT_STUB); + }); + }); + + describe("프로젝트 수정", () => { + const nid = PROJECT_STUB.nid; + + it("성공", async () => { + const serviceUpdateSpy: jest.SpyInstance = jest.spyOn(service, "update"); + serviceUpdateSpy.mockResolvedValueOnce(PROJECT_STUB); + + const result = await controller.update(nid, PROJECT_STUB); + + expect(result).toEqual(PROJECT_STUB); + expect(serviceUpdateSpy).toBeCalledTimes(1); + expect(serviceUpdateSpy).toBeCalledWith(nid, PROJECT_STUB); + }); + }); + + describe("프로젝트 삭제", () => { + it("성공", async () => { + const serviceDeleteSpy: jest.SpyInstance = jest.spyOn(service, "delete"); + serviceDeleteSpy.mockResolvedValueOnce(undefined); + + const projectId = PROJECT_STUB._id; + const result = await controller.delete(projectId); + + expect(result).toBe(true); + expect(serviceDeleteSpy).toBeCalledTimes(1); + expect(serviceDeleteSpy).toBeCalledWith(projectId); + }); + }); + + describe("프로젝트 조회", () => { + it("성공 - 단일 프로젝트", async () => { + const serviceGetByNumIdSpy: jest.SpyInstance = jest.spyOn(service, "getByNumId"); + serviceGetByNumIdSpy.mockResolvedValueOnce(PROJECT_STUB); + + const result = await controller.getByNumId(1); + + expect(result).toEqual(PROJECT_STUB); + expect(serviceGetByNumIdSpy).toBeCalledTimes(1); + expect(serviceGetByNumIdSpy).toBeCalledWith(1); + }); + + it("성공 - 전체 프로젝트", async () => { + const serviceGetAllSpy: jest.SpyInstance = jest.spyOn(service, "getAll"); + serviceGetAllSpy.mockResolvedValueOnce([PROJECT_STUB]); + + const result = await controller.getAll(); + + expect(result).toEqual([PROJECT_STUB]); + expect(serviceGetAllSpy).toBeCalledTimes(1); + }); + }); +}); diff --git a/test/routes/project/project.service.spec.ts b/test/routes/project/project.service.spec.ts new file mode 100644 index 0000000..7e88104 --- /dev/null +++ b/test/routes/project/project.service.spec.ts @@ -0,0 +1,170 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { CREATE_PROJECT_STUB, PROJECT_STUB, UPDATE_PROJECT_STUB } from "test/utils/stub"; + +import { ProjectRepository } from "@/routes/project/project.repository"; +import { ProjectService } from "@/routes/project/project.service"; +import { PROJECT_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/project/project.repository"); + +describe("ProjectService", () => { + let service: ProjectService; + let repository: ProjectRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ProjectService, ProjectRepository], + }).compile(); + + service = module.get(ProjectService); + repository = module.get(ProjectRepository); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + expect(repository).toBeDefined(); + }); + + describe("프로젝트 생성", () => { + it("성공", async () => { + const repositoryCreateSpy = jest + .spyOn(repository, "create") + .mockResolvedValueOnce(PROJECT_STUB); + + const result = await service.create(CREATE_PROJECT_STUB); + + expect(result).toEqual(PROJECT_STUB); + + expect(repositoryCreateSpy).toHaveBeenCalledTimes(1); + expect(repositoryCreateSpy).toBeCalledWith(CREATE_PROJECT_STUB); + }); + }); + + describe("프로젝트 수정", () => { + const _id = PROJECT_STUB._id; + const nid = PROJECT_STUB.nid; + + let repositoryGetOneSpy: jest.SpyInstance; + let repositoryFindOneAndUpdateSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + repositoryFindOneAndUpdateSpy = jest.spyOn(repository, "findOneAndUpdate"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(PROJECT_STUB); + repositoryFindOneAndUpdateSpy.mockResolvedValueOnce(PROJECT_STUB); + + const result = await service.update(nid, UPDATE_PROJECT_STUB); + + expect(result).toEqual(PROJECT_STUB); + + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ nid }); + + expect(repositoryFindOneAndUpdateSpy).toHaveBeenCalledTimes(1); + expect(repositoryFindOneAndUpdateSpy).toBeCalledWith({ _id }, UPDATE_PROJECT_STUB); + }); + + it("실패 - 존재하지 않는 프로젝트", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + + await expect(service.update(nid, UPDATE_PROJECT_STUB)).rejects.toThrowError( + PROJECT_ERROR.NOT_FOUND, + ); + + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ nid }); + + expect(repositoryFindOneAndUpdateSpy).toHaveBeenCalledTimes(0); + }); + }); + + describe("프로젝트 삭제", () => { + const _id = PROJECT_STUB._id; + const nid = PROJECT_STUB.nid; + + let repositoryGetOneSpy: jest.SpyInstance; + let repositoryFindOneAndDeleteSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + repositoryFindOneAndDeleteSpy = jest.spyOn(repository, "findOneAndDelete"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(PROJECT_STUB); + repositoryFindOneAndDeleteSpy.mockResolvedValueOnce(PROJECT_STUB); + + const result = await service.delete(nid); + + expect(result).toBe(PROJECT_STUB); + + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ nid }); + + expect(repositoryFindOneAndDeleteSpy).toHaveBeenCalledTimes(1); + expect(repositoryFindOneAndDeleteSpy).toBeCalledWith({ _id }); + }); + + it("실패 - 존재하지 않는 프로젝트", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + + await expect(service.delete(nid)).rejects.toThrowError(PROJECT_ERROR.NOT_FOUND); + + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ nid }); + + expect(repositoryFindOneAndDeleteSpy).toHaveBeenCalledTimes(0); + }); + }); + + describe("프로젝트 number id로 가져오기", () => { + let repositoryGetOneSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(PROJECT_STUB); + + const result = await service.getByNumId(PROJECT_STUB.nid); + + expect(result).toEqual(PROJECT_STUB); + + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ nid: PROJECT_STUB.nid }); + }); + + it("실패 - 존재하지 않는 프로젝트", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + + await expect(service.getByNumId(PROJECT_STUB.nid)).rejects.toThrowError( + PROJECT_ERROR.NOT_FOUND, + ); + + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ nid: PROJECT_STUB.nid }); + }); + }); + + describe("프로젝트 리스트 가져오기", () => { + it("성공", async () => { + const repositoryGetListSpy: jest.SpyInstance = jest + .spyOn(repository, "getAll") + .mockResolvedValueOnce([PROJECT_STUB]); + + const result = await service.getAll(); + + expect(result).toEqual([PROJECT_STUB]); + + expect(repositoryGetListSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetListSpy).toBeCalledWith({}, {}, { sort: { _id: -1 } }); + }); + }); +}); diff --git a/test/routes/series/series.controller.spec.ts b/test/routes/series/series.controller.spec.ts new file mode 100644 index 0000000..49d3900 --- /dev/null +++ b/test/routes/series/series.controller.spec.ts @@ -0,0 +1,155 @@ +import { BadRequestException, NotFoundException } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; + +import { SERIES_STUB } from "test/utils/stub"; + +import { SeriesController } from "@/routes/series/series.controller"; +import { SeriesService } from "@/routes/series/series.service"; +import { SERIES_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/series/series.service"); + +describe("SeriesController", () => { + let controller: SeriesController; + let service: SeriesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SeriesController], + providers: [SeriesService], + }).compile(); + + controller = module.get(SeriesController); + service = module.get(SeriesService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); + + describe("모든 시리즈 정보 요청", () => { + let serviceGetAllSpy: jest.SpyInstance; + + beforeEach(() => { + serviceGetAllSpy = jest.spyOn(service, "getAll"); + }); + + it("성공", async () => { + serviceGetAllSpy.mockResolvedValueOnce([SERIES_STUB]); + + const series = await controller.getAll(); + + expect(series).toEqual([SERIES_STUB]); + expect(serviceGetAllSpy).toHaveBeenCalled(); + expect(serviceGetAllSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe("특정 시리즈 정보 요청", () => { + let serviceGetByNumIdSpy: jest.SpyInstance; + + beforeEach(() => { + serviceGetByNumIdSpy = jest.spyOn(service, "getByNumId"); + }); + + it("성공", async () => { + serviceGetByNumIdSpy.mockResolvedValueOnce(SERIES_STUB); + + const series = await controller.getByNumId(SERIES_STUB.nid); + + expect(series).toEqual(SERIES_STUB); + expect(serviceGetByNumIdSpy).toHaveBeenCalled(); + expect(serviceGetByNumIdSpy).toHaveBeenCalledTimes(1); + }); + + it("실패 - 존재하지 않는 시리즈", async () => { + serviceGetByNumIdSpy.mockRejectedValueOnce(new NotFoundException(SERIES_ERROR.NOT_FOUND)); + + try { + await controller.getByNumId(SERIES_STUB.nid); + } catch (e) { + expect(e.status).toBe(404); + expect(e.message).toBe(SERIES_ERROR.NOT_FOUND); + expect(e).toBeInstanceOf(NotFoundException); + } + + expect(serviceGetByNumIdSpy).toHaveBeenCalled(); + expect(serviceGetByNumIdSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe("시리즈 정보 수정", () => { + let serviceUpdateSpy: jest.SpyInstance; + + beforeEach(() => { + serviceUpdateSpy = jest.spyOn(service, "update"); + }); + + it("성공", async () => { + serviceUpdateSpy.mockResolvedValueOnce(SERIES_STUB); + + const nid = SERIES_STUB.nid; + + const series = await controller.update(nid, SERIES_STUB); + + expect(series).toEqual(SERIES_STUB); + expect(serviceUpdateSpy).toHaveBeenCalled(); + expect(serviceUpdateSpy).toHaveBeenCalledTimes(1); + }); + + it("실패 - 존재하지 않는 시리즈", async () => { + serviceUpdateSpy.mockRejectedValueOnce(new BadRequestException(SERIES_ERROR.NOT_FOUND)); + + try { + const nid = SERIES_STUB.nid; + + await controller.update(nid, SERIES_STUB); + } catch (e) { + expect(e.status).toBe(400); + expect(e.message).toBe(SERIES_ERROR.NOT_FOUND); + expect(e).toBeInstanceOf(BadRequestException); + } + + expect(serviceUpdateSpy).toHaveBeenCalled(); + expect(serviceUpdateSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe("시리즈 정보 삭제", () => { + let serviceDeleteSpy: jest.SpyInstance; + + beforeEach(() => { + serviceDeleteSpy = jest.spyOn(service, "delete"); + }); + + it("성공", async () => { + serviceDeleteSpy.mockResolvedValueOnce(undefined); + const nid = SERIES_STUB.nid; + + const series = await controller.delete(nid); + + expect(series).toEqual(true); + expect(serviceDeleteSpy).toHaveBeenCalled(); + expect(serviceDeleteSpy).toHaveBeenCalledTimes(1); + }); + + it("실패 - 존재하지 않는 시리즈", async () => { + serviceDeleteSpy.mockRejectedValueOnce(new BadRequestException(SERIES_ERROR.NOT_FOUND)); + + try { + const nid = SERIES_STUB.nid; + + await controller.delete(nid); + } catch (e) { + expect(e.status).toBe(400); + expect(e.message).toBe(SERIES_ERROR.NOT_FOUND); + expect(e).toBeInstanceOf(BadRequestException); + } + + expect(serviceDeleteSpy).toHaveBeenCalled(); + expect(serviceDeleteSpy).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/test/routes/series/series.service.spec.ts b/test/routes/series/series.service.spec.ts new file mode 100644 index 0000000..07252ed --- /dev/null +++ b/test/routes/series/series.service.spec.ts @@ -0,0 +1,283 @@ +import { BadRequestException, NotFoundException } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; + +import { + SERIES_CREATE_INPUT_STUB, + SERIES_STUB, + SERIES_STUB_WITHOUT_POSTS, + SERIES_UPDATE_INPUT_STUB, +} from "test/utils/stub"; + +import { PostRepository } from "@/routes/post/post.repository"; +import { SeriesRepository } from "@/routes/series/series.repository"; +import { SeriesService } from "@/routes/series/series.service"; +import { SERIES_ERROR, SERIES_FIND_OPTIONS, SERIES_FIND_PROJECTION } from "@/utils/constants"; + +jest.mock("@/routes/post/post.repository"); +jest.mock("@/routes/series/series.repository"); +jest.mock("@/common/decorators/transaction.decorator", () => ({ + Transactional: () => { + return jest.fn(); + }, +})); + +describe("SeriesService", () => { + let service: SeriesService; + let postRepository: PostRepository; + let repository: SeriesRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SeriesService, PostRepository, SeriesRepository], + }).compile(); + + service = module.get(SeriesService); + postRepository = module.get(PostRepository); + repository = module.get(SeriesRepository); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); + + describe("시리즈 생성", () => { + let repositoryCreateSpy: jest.SpyInstance; + let repositoryGetOneSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryCreateSpy = jest.spyOn(repository, "create"); + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + }); + + it("성공", async () => { + repositoryCreateSpy.mockResolvedValueOnce(SERIES_STUB_WITHOUT_POSTS); + + const series = await service.create(SERIES_CREATE_INPUT_STUB); + + expect(series).toEqual(SERIES_STUB_WITHOUT_POSTS); + expect(repositoryCreateSpy).toBeCalledTimes(1); + expect(repositoryCreateSpy).toBeCalledWith(SERIES_CREATE_INPUT_STUB); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ name: SERIES_CREATE_INPUT_STUB.name }); + }); + + describe("실패", () => { + it("이미 존재하는 시리즈", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(SERIES_STUB); + + try { + await service.create(SERIES_CREATE_INPUT_STUB); + } catch (e) { + expect(e.status).toBe(400); + expect(e.message).toBe(SERIES_ERROR.ALREADY_EXISTS); + expect(e).toBeInstanceOf(BadRequestException); + expect(repositoryCreateSpy).toBeCalledTimes(0); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ name: SERIES_CREATE_INPUT_STUB.name }); + } + }); + }); + }); + + describe("시리즈 전체 수정", () => { + const nid = SERIES_STUB.nid; + const _id = SERIES_STUB._id; + const GET_ONE_ARGS = [{ nid }, SERIES_FIND_PROJECTION, SERIES_FIND_OPTIONS]; + + let repositoryFindOneAndUpdateSpy: jest.SpyInstance; + let repositoryGetOneSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryFindOneAndUpdateSpy = jest.spyOn(repository, "findOneAndUpdate"); + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(SERIES_STUB); + repositoryFindOneAndUpdateSpy.mockResolvedValueOnce(SERIES_STUB); + + const series = await service.update(nid, SERIES_UPDATE_INPUT_STUB); + + expect(series).toEqual(SERIES_STUB); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(...GET_ONE_ARGS); + expect(repositoryFindOneAndUpdateSpy).toBeCalledTimes(1); + expect(repositoryFindOneAndUpdateSpy).toBeCalledWith({ _id }, SERIES_UPDATE_INPUT_STUB); + }); + + it("실패", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + + try { + await service.update(nid, SERIES_STUB); + } catch (e) { + expect(e.status).toBe(404); + expect(e.message).toBe(SERIES_ERROR.NOT_FOUND); + expect(e).toBeInstanceOf(NotFoundException); + + expect(repositoryFindOneAndUpdateSpy).toBeCalledTimes(0); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(...GET_ONE_ARGS); + } + }); + }); + + describe("시리즈 수정 - 게시글 추가", () => { + let repositoryFindOrCreateSpy: jest.SpyInstance; + let repositoryPushPostId: jest.SpyInstance; + + beforeEach(() => { + repositoryFindOrCreateSpy = jest.spyOn(repository, "findOrCreate"); + repositoryPushPostId = jest.spyOn(repository, "pushPostIdInSeries"); + }); + + it("성공", async () => { + repositoryFindOrCreateSpy.mockResolvedValueOnce(SERIES_STUB); + repositoryPushPostId.mockResolvedValueOnce(undefined); + + const seriesId = SERIES_STUB._id; + const seriesName = SERIES_STUB.name; + const postId = SERIES_STUB.posts[0]._id; + + const series = await service.pushPostIdInSeries(seriesName, postId); + + expect(series).toEqual(SERIES_STUB); + expect(repositoryPushPostId).toBeCalledTimes(1); + expect(repositoryPushPostId).toBeCalledWith(seriesId, postId); + expect(repositoryFindOrCreateSpy).toBeCalledTimes(1); + expect(repositoryFindOrCreateSpy).toBeCalledWith(seriesName); + }); + }); + + describe("시리즈 수정 - 게시글 제거", () => { + let repositoryGetOneSpy: jest.SpyInstance; + let repositoryPullPostIdSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + repositoryPullPostIdSpy = jest.spyOn(repository, "pullPostIdInSeries"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(SERIES_STUB); + repositoryPullPostIdSpy.mockResolvedValueOnce(undefined); + + const seriesId = SERIES_STUB._id; + const seriesName = SERIES_STUB.name; + const postId = SERIES_STUB.posts[0]._id; + + await service.pullPostIdInSeries(seriesName, postId); + + expect(repositoryPullPostIdSpy).toBeCalledTimes(1); + expect(repositoryPullPostIdSpy).toBeCalledWith(seriesId, postId); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ name: seriesName }); + }); + }); + + describe("시리즈 삭제", () => { + const nid = SERIES_STUB.nid; + const GET_ONE_ARGS = [{ nid }, SERIES_FIND_PROJECTION, SERIES_FIND_OPTIONS]; + + let postRepositoryDeleteSeriesSpy: jest.SpyInstance; + let repositoryDeleteSpy: jest.SpyInstance; + let repositoryGetOneSpy: jest.SpyInstance; + + beforeEach(() => { + postRepositoryDeleteSeriesSpy = jest.spyOn(postRepository, "deleteSeriesInPosts"); + repositoryDeleteSpy = jest.spyOn(repository, "delete"); + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + }); + + it("성공", async () => { + postRepositoryDeleteSeriesSpy.mockResolvedValueOnce(undefined); + repositoryGetOneSpy.mockResolvedValueOnce(SERIES_STUB); + repositoryDeleteSpy.mockResolvedValueOnce(SERIES_STUB); + + const isCompleted = await service.delete(nid); + + expect(isCompleted).toEqual(true); + + expect(postRepositoryDeleteSeriesSpy).toBeCalledTimes(1); + expect(postRepositoryDeleteSeriesSpy).toBeCalledWith(SERIES_STUB._id); + + expect(repositoryDeleteSpy).toBeCalledTimes(1); + expect(repositoryDeleteSpy).toBeCalledWith(SERIES_STUB._id); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(...GET_ONE_ARGS); + }); + + describe("실패", () => { + it("존재하지 않는 시리즈", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + + try { + await service.delete(nid); + } catch (e) { + expect(e.status).toBe(404); + expect(e.message).toBe(SERIES_ERROR.NOT_FOUND); + expect(e).toBeInstanceOf(NotFoundException); + + expect(repositoryDeleteSpy).toBeCalledTimes(0); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(...GET_ONE_ARGS); + } + }); + }); + }); + + describe("시리즈 조회", () => { + // 전체 조회 + // number id를 통해 조회 + const args = [{ nid: SERIES_STUB.nid }, SERIES_FIND_PROJECTION, SERIES_FIND_OPTIONS]; + + let repositoryGetAllSpy: jest.SpyInstance; + let repositoryGetOneSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetAllSpy = jest.spyOn(repository, "getAll"); + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + }); + + describe("성공", () => { + it("전체 조회", async () => { + repositoryGetAllSpy.mockResolvedValueOnce([SERIES_STUB]); + + const series = await service.getAll(); + + expect(series).toEqual([SERIES_STUB]); + expect(repositoryGetAllSpy).toBeCalledTimes(1); + }); + + it("number id를 통해 조회", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(SERIES_STUB); + + const series = await service.getByNumId(SERIES_STUB.nid); + + expect(series).toEqual(SERIES_STUB); + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(...args); + }); + }); + + describe("실패", () => { + it("number id를 통해 조회 - 존재하지 않는 시리즈", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + + try { + await service.getByNumId(SERIES_STUB.nid); + } catch (e) { + expect(e.status).toBe(404); + expect(e.message).toBe(SERIES_ERROR.NOT_FOUND); + expect(e).toBeInstanceOf(NotFoundException); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith(...args); + } + }); + }); + }); +}); diff --git a/test/routes/skill/skill.controller.spec.ts b/test/routes/skill/skill.controller.spec.ts new file mode 100644 index 0000000..8e2cd76 --- /dev/null +++ b/test/routes/skill/skill.controller.spec.ts @@ -0,0 +1,84 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { CREATE_SKILL_STUB, SKILLS_STUB_BY_SKILL_TYPE, SKILL_STUB } from "test/utils/stub"; + +import { SkillController } from "@/routes/skill/skill.controller"; +import { SkillService } from "@/routes/skill/skill.service"; + +jest.mock("@/routes/skill/skill.service"); + +describe("SkillController", () => { + let controller: SkillController; + let service: SkillService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SkillController], + providers: [SkillService], + }).compile(); + + controller = module.get(SkillController); + service = module.get(SkillService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + expect(service).toBeDefined(); + }); + + describe("스킬 생성", () => { + it("성공", async () => { + const serviceCreateSpy = jest.spyOn(service, "create").mockResolvedValueOnce(SKILL_STUB); + + const result = await controller.create(CREATE_SKILL_STUB); + + expect(result).toEqual(SKILL_STUB); + + expect(serviceCreateSpy).toBeCalledTimes(1); + expect(serviceCreateSpy).toBeCalledWith(CREATE_SKILL_STUB); + }); + }); + + describe("스킬 수정", () => { + it("성공", async () => { + const serviceUpdateSpy = jest.spyOn(service, "update").mockResolvedValueOnce(SKILL_STUB); + + const result = await controller.update(SKILL_STUB._id, CREATE_SKILL_STUB); + + expect(result).toEqual(SKILL_STUB); + + expect(serviceUpdateSpy).toBeCalledTimes(1); + expect(serviceUpdateSpy).toBeCalledWith(SKILL_STUB._id, CREATE_SKILL_STUB); + }); + }); + + describe("스킬 삭제", () => { + it("성공", async () => { + const serviceDeleteSpy = jest.spyOn(service, "delete").mockResolvedValueOnce(); + + const result = await controller.delete(SKILL_STUB._id); + + expect(result).toEqual(true); + + expect(serviceDeleteSpy).toBeCalledTimes(1); + expect(serviceDeleteSpy).toBeCalledWith(SKILL_STUB._id); + }); + }); + + describe("스킬 조회", () => { + it("성공", async () => { + const serviceGetAllSpy = jest + .spyOn(service, "getAll") + .mockResolvedValueOnce(SKILLS_STUB_BY_SKILL_TYPE); + + const result = await controller.getAll(); + + expect(result).toEqual(SKILLS_STUB_BY_SKILL_TYPE); + + expect(serviceGetAllSpy).toBeCalledTimes(1); + expect(serviceGetAllSpy).toBeCalledWith(); + }); + }); +}); diff --git a/test/routes/skill/skill.service.spec.ts b/test/routes/skill/skill.service.spec.ts new file mode 100644 index 0000000..2978e51 --- /dev/null +++ b/test/routes/skill/skill.service.spec.ts @@ -0,0 +1,189 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { + CREATE_SKILL_STUB, + SKILLS_STUB_BY_SKILL_TYPE, + SKILL_STUB, + UPDATE_SKILL_STUB, +} from "test/utils/stub"; + +import { SkillRepository } from "@/routes/skill/skill.repository"; +import { SkillService } from "@/routes/skill/skill.service"; +import { SKILL_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/skill/skill.repository"); + +describe("SkillService", () => { + let service: SkillService; + let repository: SkillRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SkillService, SkillRepository], + }).compile(); + + service = module.get(SkillService); + repository = module.get(SkillRepository); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + expect(repository).toBeDefined(); + }); + + describe("스킬 생성", () => { + let createSpy: jest.SpyInstance; + let getOneSpy: jest.SpyInstance; + + const GET_ALL_PARAMS = { name: CREATE_SKILL_STUB.name }; + + beforeEach(() => { + createSpy = jest.spyOn(repository, "create"); + getOneSpy = jest.spyOn(repository, "getOne"); + }); + + it("성공", async () => { + getOneSpy.mockResolvedValue(null); + createSpy.mockResolvedValue(SKILL_STUB); + + const result = await service.create(CREATE_SKILL_STUB); + + expect(result).toEqual(SKILL_STUB); + + expect(getOneSpy).toBeCalledTimes(1); + expect(getOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(createSpy).toBeCalledTimes(1); + expect(createSpy).toBeCalledWith(CREATE_SKILL_STUB); + }); + + it("실패 - 동일한 이름을 가진 스킬이 존재", async () => { + getOneSpy.mockResolvedValue(SKILL_STUB); + + await expect(service.create(CREATE_SKILL_STUB)).rejects.toThrowError( + SKILL_ERROR.ALREADY_EXISTS, + ); + + expect(getOneSpy).toBeCalledTimes(1); + expect(getOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(createSpy).not.toBeCalled(); + }); + }); + + describe("스킬 수정", () => { + let getByIdSpy: jest.SpyInstance; + let getOneSpy: jest.SpyInstance; + let findOneAndUpdateSpy: jest.SpyInstance; + + const skillId = SKILL_STUB._id; + const GET_ALL_PARAMS = { name: SKILL_STUB.name, _id: { $ne: skillId } }; + + beforeEach(() => { + getByIdSpy = jest.spyOn(repository, "getById"); + getOneSpy = jest.spyOn(repository, "getOne"); + findOneAndUpdateSpy = jest.spyOn(repository, "findOneAndUpdate"); + }); + + it("성공", async () => { + getByIdSpy.mockResolvedValue(SKILL_STUB); + getOneSpy.mockResolvedValue(null); + findOneAndUpdateSpy.mockResolvedValue(SKILL_STUB); + + const result = await service.update(skillId, UPDATE_SKILL_STUB); + + expect(result).toEqual(SKILL_STUB); + + expect(getByIdSpy).toBeCalledTimes(1); + expect(getByIdSpy).toBeCalledWith(skillId); + + expect(getOneSpy).toBeCalledTimes(1); + expect(getOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(findOneAndUpdateSpy).toBeCalledTimes(1); + expect(findOneAndUpdateSpy).toBeCalledWith({ _id: skillId }, UPDATE_SKILL_STUB); + }); + + it("실패 - 존재하지 않는 스킬", async () => { + getByIdSpy.mockResolvedValue(null); + + await expect(service.update(skillId, UPDATE_SKILL_STUB)).rejects.toThrowError( + SKILL_ERROR.NOT_FOUND, + ); + + expect(getByIdSpy).toBeCalledTimes(1); + expect(getByIdSpy).toBeCalledWith(skillId); + + expect(getOneSpy).not.toBeCalled(); + expect(findOneAndUpdateSpy).not.toBeCalled(); + }); + + it("실패 - 동일한 이름을 가진 스킬이 존재", async () => { + getByIdSpy.mockResolvedValue(SKILL_STUB); + getOneSpy.mockResolvedValue(SKILL_STUB); + + await expect(service.update(skillId, UPDATE_SKILL_STUB)).rejects.toThrowError( + SKILL_ERROR.ALREADY_EXISTS, + ); + + expect(getByIdSpy).toBeCalledTimes(1); + expect(getByIdSpy).toBeCalledWith(skillId); + + expect(getOneSpy).toBeCalledTimes(1); + expect(getOneSpy).toBeCalledWith(GET_ALL_PARAMS); + + expect(findOneAndUpdateSpy).not.toBeCalled(); + }); + }); + + describe("스킬 삭제", () => { + let getByIdSpy: jest.SpyInstance; + let deleteSpy: jest.SpyInstance; + + beforeEach(() => { + getByIdSpy = jest.spyOn(repository, "getById"); + deleteSpy = jest.spyOn(repository, "delete"); + }); + + it("성공", async () => { + getByIdSpy.mockResolvedValue(SKILL_STUB); + deleteSpy.mockResolvedValue(SKILL_STUB); + + const result = await service.delete(SKILL_STUB._id); + + expect(result).toEqual(SKILL_STUB); + + expect(getByIdSpy).toBeCalledTimes(1); + expect(getByIdSpy).toBeCalledWith(SKILL_STUB._id); + + expect(deleteSpy).toBeCalledTimes(1); + expect(deleteSpy).toBeCalledWith(SKILL_STUB._id); + }); + + it("실패 - 존재하지 않는 스킬", async () => { + getByIdSpy.mockResolvedValue(null); + + await expect(service.delete(SKILL_STUB._id)).rejects.toThrowError(SKILL_ERROR.NOT_FOUND); + + expect(getByIdSpy).toBeCalledTimes(1); + expect(getByIdSpy).toBeCalledWith(SKILL_STUB._id); + + expect(deleteSpy).not.toBeCalled(); + }); + }); + + describe("스킬 전체 조회", () => { + it("성공", async () => { + const getAllSpy = jest.spyOn(repository, "getAllToSkillType"); + getAllSpy.mockResolvedValue(SKILLS_STUB_BY_SKILL_TYPE); + + const result = await service.getAll(); + + expect(result).toEqual(SKILLS_STUB_BY_SKILL_TYPE); + + expect(getAllSpy).toBeCalledTimes(1); + }); + }); +}); diff --git a/test/routes/tag/tag.controller.spec.ts b/test/routes/tag/tag.controller.spec.ts new file mode 100644 index 0000000..01d75b9 --- /dev/null +++ b/test/routes/tag/tag.controller.spec.ts @@ -0,0 +1,52 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { TAG_STUB } from "test/utils/stub"; + +import { TagController } from "@/routes/tag/tag.controller"; +import { TagService } from "@/routes/tag/tag.service"; + +jest.mock("@/routes/tag/tag.service"); + +describe("TagController", () => { + let controller: TagController; + let service: TagService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [TagController], + providers: [TagService], + }).compile(); + + controller = module.get(TagController); + service = module.get(TagService); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); + + it("태그 전체 조회", async () => { + const serviceGetAllSpy: jest.SpyInstance = jest.spyOn(service, "getAll"); + serviceGetAllSpy.mockResolvedValueOnce([TAG_STUB]); + + const tags = await controller.getAll(); + + expect(tags).toEqual([TAG_STUB]); + expect(serviceGetAllSpy).toHaveBeenCalled(); + expect(serviceGetAllSpy).toHaveBeenCalledTimes(1); + }); + + it("태그 이름으로 조회", async () => { + const serviceGetByNameSpy: jest.SpyInstance = jest.spyOn(service, "getByName"); + serviceGetByNameSpy.mockResolvedValueOnce(TAG_STUB); + + const tag = await controller.getByName("태그 이름"); + + expect(tag).toEqual(TAG_STUB); + expect(serviceGetByNameSpy).toHaveBeenCalled(); + expect(serviceGetByNameSpy).toHaveBeenCalledTimes(1); + expect(serviceGetByNameSpy).toHaveBeenCalledWith("태그 이름"); + }); +}); diff --git a/test/routes/tag/tag.service.spec.ts b/test/routes/tag/tag.service.spec.ts new file mode 100644 index 0000000..21cf084 --- /dev/null +++ b/test/routes/tag/tag.service.spec.ts @@ -0,0 +1,129 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { TAG_STUB } from "test/utils/stub"; + +import { TagRepository } from "@/routes/tag/tag.repository"; +import { TagService } from "@/routes/tag/tag.service"; + +jest.mock("@/routes/tag/tag.repository"); +jest.mock("@/common/decorators/transaction.decorator", () => ({ + Transactional: () => { + return jest.fn(); + }, +})); + +describe("TagService", () => { + let service: TagService; + let repository: TagRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [TagService, TagRepository], + }).compile(); + + service = module.get(TagService); + repository = module.get(TagRepository); + + jest.clearAllMocks(); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); + + describe("태그 조회", () => { + it("태그 전체 조회", async () => { + const repositoryGetAllSpy: jest.SpyInstance = jest.spyOn(repository, "getAllToA"); + repositoryGetAllSpy.mockResolvedValueOnce([TAG_STUB]); + + const tags = await service.getAll(); + + expect(tags).toEqual([TAG_STUB]); + expect(repositoryGetAllSpy).toHaveBeenCalled(); + expect(repositoryGetAllSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetAllSpy).toHaveBeenCalledWith(); + }); + + it("태그 이름으로 조회", async () => { + const repositoryGetOneSpy: jest.SpyInstance = jest.spyOn(repository, "getOne"); + repositoryGetOneSpy.mockResolvedValueOnce(TAG_STUB); + + const tag = await service.getByName(TAG_STUB.name); + + expect(tag).toEqual(TAG_STUB); + expect(repositoryGetOneSpy).toHaveBeenCalled(); + expect(repositoryGetOneSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetOneSpy).toHaveBeenCalledWith( + { name: TAG_STUB.name }, + { name: 1, postCount: { $size: "$posts" } }, + { populate: "posts" }, + ); + }); + }); + + describe("태그에 게시글 추가", () => { + it("성공", async () => { + const repositoryFindOrCreateSpy: jest.SpyInstance = jest.spyOn(repository, "findOrCreate"); + const repositoryAddPostIdInTagsSpy: jest.SpyInstance = jest.spyOn( + repository, + "addPostIdInTags", + ); + repositoryFindOrCreateSpy.mockResolvedValueOnce(TAG_STUB); + repositoryAddPostIdInTagsSpy.mockResolvedValueOnce(TAG_STUB); + + const tags = await service.pushPostIdInTags([TAG_STUB.name], TAG_STUB.posts[0]._id); + + expect(tags).toEqual([TAG_STUB]); + + expect(repositoryFindOrCreateSpy).toHaveBeenCalled(); + expect(repositoryFindOrCreateSpy).toHaveBeenCalledTimes(1); + expect(repositoryFindOrCreateSpy).toHaveBeenCalledWith(TAG_STUB.name); + + expect(repositoryAddPostIdInTagsSpy).toHaveBeenCalled(); + expect(repositoryAddPostIdInTagsSpy).toHaveBeenCalledTimes(1); + expect(repositoryAddPostIdInTagsSpy).toHaveBeenCalledWith([TAG_STUB], TAG_STUB.posts[0]._id); + }); + }); + + describe("태그에 게시글 삭제", () => { + it("태그 이름 배열을 받아 해당 이름과 일치하는 태그를 찾아 게시글 아이디를 삭제한다", async () => { + const repositoryPullPostIdInTagsSpy: jest.SpyInstance = jest.spyOn( + repository, + "pullPostIdInTags", + ); + const repositoryGetAllSpy: jest.SpyInstance = jest.spyOn(repository, "getAll"); + + repositoryPullPostIdInTagsSpy.mockResolvedValueOnce(true); + repositoryGetAllSpy.mockResolvedValueOnce([TAG_STUB]); + + const tags = await service.pullPostIdInTags([TAG_STUB.name], TAG_STUB.posts[0]._id); + + expect(tags).toEqual([TAG_STUB]); + + expect(repositoryPullPostIdInTagsSpy).toHaveBeenCalled(); + expect(repositoryPullPostIdInTagsSpy).toHaveBeenCalledTimes(1); + expect(repositoryPullPostIdInTagsSpy).toHaveBeenCalledWith( + [TAG_STUB.name], + TAG_STUB.posts[0]._id, + ); + + expect(repositoryGetAllSpy).toHaveBeenCalled(); + expect(repositoryGetAllSpy).toHaveBeenCalledTimes(1); + expect(repositoryGetAllSpy).toHaveBeenCalledWith({ name: { $in: [TAG_STUB.name] } }); + }); + + it("게시글 아이디를 받아 해당 게시글을 가지고 있는 태그를 찾아 게시글 아이디를 삭제한다", async () => { + const repositoryPullPostIdByPostIdSpy: jest.SpyInstance = jest.spyOn( + repository, + "pullPostIdByPostId", + ); + repositoryPullPostIdByPostIdSpy.mockResolvedValueOnce(undefined); + + await service.pullPostIdByPostId(TAG_STUB.posts[0]._id); + + expect(repositoryPullPostIdByPostIdSpy).toHaveBeenCalled(); + expect(repositoryPullPostIdByPostIdSpy).toHaveBeenCalledTimes(1); + expect(repositoryPullPostIdByPostIdSpy).toHaveBeenCalledWith(TAG_STUB.posts[0]._id); + }); + }); +}); diff --git a/test/routes/user/user.controller.spec.ts b/test/routes/user/user.controller.spec.ts new file mode 100644 index 0000000..e1e83be --- /dev/null +++ b/test/routes/user/user.controller.spec.ts @@ -0,0 +1,110 @@ +import { BadRequestException, ConflictException } from "@nestjs/common"; +import { TestingModule } from "@nestjs/testing"; + +import createTestingModule from "test/utils/mongo/createTestModule"; +import { USER_INPUT_STUB, USER_STUB, USER_STUB_NON_PASSWORD } from "test/utils/stub"; + +import { UserController } from "@/routes/user/user.controller"; +import { UserService } from "@/routes/user/user.service"; +import { USER_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/user/user.service"); + +describe("UserController", () => { + let controller: UserController; + let service: UserService; + + beforeEach(async () => { + const module: TestingModule = await createTestingModule({ + controllers: [UserController], + providers: [UserService], + }); + + controller = module.get(UserController); + service = module.get(UserService); + + jest.clearAllMocks(); + }); + + describe("유저 생성", () => { + let serviceCreateSpy: jest.SpyInstance; + + beforeEach(() => { + serviceCreateSpy = jest.spyOn(service, "create"); + }); + + it("성공", async () => { + serviceCreateSpy.mockResolvedValueOnce(USER_STUB); + + const user = await controller.createUser(USER_INPUT_STUB); + + expect(serviceCreateSpy).toHaveBeenCalledWith(USER_INPUT_STUB); + expect(user).toEqual({ username: USER_STUB.username }); + }); + + describe("실패", () => { + it("이미 존재하는 유저", async () => { + serviceCreateSpy.mockRejectedValueOnce(new ConflictException(USER_ERROR.ALREADY_EXISTS)); + + try { + await controller.createUser(USER_INPUT_STUB); + } catch (e) { + expect(e.status).toBe(409); + expect(e.message).toBe(USER_ERROR.ALREADY_EXISTS); + expect(e).toBeInstanceOf(ConflictException); + } + + expect(serviceCreateSpy).toHaveBeenCalledWith(USER_INPUT_STUB); + }); + + it("올바르지 않은 유저 정보", async () => { + serviceCreateSpy.mockRejectedValueOnce(new BadRequestException(USER_ERROR.BAD_REQUEST)); + + try { + await controller.createUser(USER_INPUT_STUB); + } catch (e) { + expect(e.status).toBe(400); + expect(e.message).toBe(USER_ERROR.BAD_REQUEST); + expect(e).toBeInstanceOf(BadRequestException); + } + + expect(serviceCreateSpy).toHaveBeenCalledWith(USER_INPUT_STUB); + }); + }); + }); + + describe("유저 조회", () => { + const TOKEN_USER = { _id: USER_STUB._id }; + + let serviceGetByIdSpy: jest.SpyInstance; + + beforeEach(() => { + serviceGetByIdSpy = jest.spyOn(service, "getById"); + }); + + it("성공", async () => { + serviceGetByIdSpy.mockResolvedValueOnce(USER_STUB_NON_PASSWORD); + + const user = await controller.getUser(TOKEN_USER); + + expect(serviceGetByIdSpy).toHaveBeenCalledWith(USER_STUB._id); + expect(user).toEqual({ username: USER_STUB.username }); + }); + + describe("실패", () => { + it("존재하지 않는 유저", async () => { + serviceGetByIdSpy.mockRejectedValueOnce(new BadRequestException(USER_ERROR.BAD_REQUEST)); + + try { + await controller.getUser(TOKEN_USER); + } catch (e) { + expect(e.status).toBe(400); + expect(e.message).toBe(USER_ERROR.BAD_REQUEST); + expect(e).toBeInstanceOf(BadRequestException); + } + + expect(serviceGetByIdSpy).toHaveBeenCalledWith(USER_STUB._id); + }); + }); + }); +}); diff --git a/test/routes/user/user.service.spec.ts b/test/routes/user/user.service.spec.ts new file mode 100644 index 0000000..954f33b --- /dev/null +++ b/test/routes/user/user.service.spec.ts @@ -0,0 +1,99 @@ +import { BadRequestException } from "@nestjs/common"; +import { TestingModule } from "@nestjs/testing"; + +import createTestingModule from "test/utils/mongo/createTestModule"; +import { USER_INPUT_STUB, USER_STUB, USER_STUB_NON_PASSWORD } from "test/utils/stub"; + +import { UserRepository } from "@/routes/user/user.repository"; +import { UserService } from "@/routes/user/user.service"; +import { USER_ERROR } from "@/utils/constants"; + +jest.mock("@/routes/user/user.repository"); + +describe("UserService", () => { + let service: UserService; + let repository: UserRepository; + + beforeEach(async () => { + const module: TestingModule = await createTestingModule({ + providers: [UserService, UserRepository], + }); + + service = module.get(UserService); + repository = module.get(UserRepository); + + jest.clearAllMocks(); + }); + + describe("유저 생성", () => { + let repositoryGetOneSpy: jest.SpyInstance; + let repositoryCreateSpy: jest.SpyInstance; + + beforeEach(() => { + repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + repositoryCreateSpy = jest.spyOn(repository, "create"); + }); + + it("성공", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(null); + repositoryCreateSpy.mockResolvedValueOnce(USER_STUB_NON_PASSWORD); + + const user = await service.create(USER_INPUT_STUB); + + expect(user).toEqual(USER_STUB_NON_PASSWORD); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ userId: USER_INPUT_STUB.userId }); + + expect(repositoryCreateSpy).toBeCalledTimes(1); + expect(repositoryCreateSpy).toBeCalledWith(USER_INPUT_STUB); + }); + + describe("실패", () => { + it("이미 존재하는 유저", async () => { + repositoryGetOneSpy.mockResolvedValueOnce(USER_STUB); + + try { + await service.create(USER_STUB); + } catch (e) { + expect(e.status).toBe(400); + expect(e.message).toBe(USER_ERROR.ALREADY_EXISTS); + expect(e).toBeInstanceOf(BadRequestException); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ userId: USER_INPUT_STUB.userId }); + + expect(repositoryCreateSpy).not.toBeCalled(); + } + }); + }); + }); + + describe("유저 조회", () => { + it("유저 아이디로 조회", async () => { + const repositoryGetOneSpy = jest.spyOn(repository, "getOne"); + repositoryGetOneSpy.mockResolvedValueOnce(USER_STUB); + + const user = await service.getByUserId(USER_STUB.userId); + + expect(user).toEqual(USER_STUB); + expect(user.password).toBeDefined(); + + expect(repositoryGetOneSpy).toBeCalledTimes(1); + expect(repositoryGetOneSpy).toBeCalledWith({ userId: USER_STUB.userId }); + }); + + it("unique id로 조회", async () => { + const repositoryGetByIdSpy = jest.spyOn(repository, "getById"); + repositoryGetByIdSpy.mockResolvedValueOnce(USER_STUB_NON_PASSWORD); + + const user = await service.getById(USER_STUB._id); + + expect(user).toEqual(USER_STUB_NON_PASSWORD); + expect(user.password).toBeUndefined(); + + expect(repositoryGetByIdSpy).toBeCalledTimes(1); + expect(repositoryGetByIdSpy).toBeCalledWith(USER_STUB._id, { password: 0 }); + }); + }); +}); diff --git a/test/utils/mock/auth.ts b/test/utils/mock/auth.ts new file mode 100644 index 0000000..428d82b --- /dev/null +++ b/test/utils/mock/auth.ts @@ -0,0 +1,8 @@ +import { Response } from "express"; + +const RESPONSE_MOCK = { + cookie: jest.fn(), + clearCookie: jest.fn(), +} as unknown as Response; + +export { RESPONSE_MOCK }; diff --git a/test/utils/mock/index.ts b/test/utils/mock/index.ts new file mode 100644 index 0000000..b306ff1 --- /dev/null +++ b/test/utils/mock/index.ts @@ -0,0 +1,2 @@ +export * from "./user"; +export * from "./auth"; diff --git a/test/utils/mock/user.ts b/test/utils/mock/user.ts new file mode 100644 index 0000000..7305adb --- /dev/null +++ b/test/utils/mock/user.ts @@ -0,0 +1,18 @@ +import { USER_STUB, USER_STUB_NON_PASSWORD } from "test/utils/stub"; + +const MockUserService = jest.fn().mockReturnValue({ + create: jest.fn().mockResolvedValue(USER_STUB), + getByUserId: jest.fn().mockResolvedValue(USER_STUB), + getById: jest.fn().mockResolvedValue(USER_STUB_NON_PASSWORD), + updateRefreshToken: jest.fn(), +}); + +const MockUserRepository = jest.fn().mockReturnValue({ + create: jest.fn().mockResolvedValue(USER_STUB), + getByUserId: jest.fn().mockResolvedValue(USER_STUB), + getById: jest.fn().mockResolvedValue(USER_STUB_NON_PASSWORD), + hashPassword: jest.fn().mockResolvedValue("hashedPassword"), + updateRefreshToken: jest.fn(), +}); + +export { MockUserService, MockUserRepository }; diff --git a/test/utils/mongo/createTestModule.ts b/test/utils/mongo/createTestModule.ts new file mode 100644 index 0000000..3b592ed --- /dev/null +++ b/test/utils/mongo/createTestModule.ts @@ -0,0 +1,12 @@ +import { ModuleMetadata } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; + +async function createTestingModule(metadata: ModuleMetadata = {}): Promise { + const testModule: TestingModule = await Test.createTestingModule({ + ...metadata, + }).compile(); + + return testModule; +} + +export default createTestingModule; diff --git a/test/utils/mongo/mongodb-memory-server-setup.ts b/test/utils/mongo/mongodb-memory-server-setup.ts new file mode 100644 index 0000000..050754b --- /dev/null +++ b/test/utils/mongo/mongodb-memory-server-setup.ts @@ -0,0 +1,15 @@ +import { MongoMemoryServer as MS } from "mongodb-memory-server"; + +export class MongoMemoryServer { + static mongoUri: string; + private static mongoServer: MS; + + static async start() { + this.mongoServer = await MS.create(); + this.mongoUri = this.mongoServer.getUri(); + } + + static async stop() { + await this.mongoServer.stop(); + } +} diff --git a/test/utils/stub/auth.ts b/test/utils/stub/auth.ts new file mode 100644 index 0000000..5aa7491 --- /dev/null +++ b/test/utils/stub/auth.ts @@ -0,0 +1,3 @@ +const TOKEN_STUB = "test_token"; + +export { TOKEN_STUB }; diff --git a/test/utils/stub/experience.ts b/test/utils/stub/experience.ts new file mode 100644 index 0000000..2c5f971 --- /dev/null +++ b/test/utils/stub/experience.ts @@ -0,0 +1,20 @@ +import { CreateExperienceDto } from "@/routes/experience/dto/create-experience.dto"; +import { ExperienceDocument } from "@/routes/experience/experience.schema"; + +const CREATE_EXPERIENCE_STUB: CreateExperienceDto = { + title: "경력1", + description: "경력1", + start: "2021-01-01", + end: "2021-01-01", +}; + +const UPDATE_EXPERIENCE_STUB = { + ...CREATE_EXPERIENCE_STUB, + _id: "_id", +}; + +const EXPERIENCE_STUB = { + ...UPDATE_EXPERIENCE_STUB, +} as unknown as ExperienceDocument; + +export { CREATE_EXPERIENCE_STUB, UPDATE_EXPERIENCE_STUB, EXPERIENCE_STUB }; diff --git a/test/utils/stub/index.ts b/test/utils/stub/index.ts new file mode 100644 index 0000000..34910a1 --- /dev/null +++ b/test/utils/stub/index.ts @@ -0,0 +1,8 @@ +export * from "./user"; +export * from "./auth"; +export * from "./tag"; +export * from "./series"; +export * from "./post"; +export * from "./project"; +export * from "./experience"; +export * from "./skill"; diff --git a/test/utils/stub/post.ts b/test/utils/stub/post.ts new file mode 100644 index 0000000..eb0d277 --- /dev/null +++ b/test/utils/stub/post.ts @@ -0,0 +1,52 @@ +import { SERIES_STUB } from "test/utils/stub"; +import { TAG_STUB } from "test/utils/stub"; + +import { CreatePostDto } from "@/routes/post/dto/create-post.dto"; +import { GetPostsDto } from "@/routes/post/dto/get-posts.dto"; +import { UpdatePostDto } from "@/routes/post/dto/update-post.dto"; + +const POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES = { + title: "title", + content: "content", + thumbnail: "thumbnail", +}; + +const POST_CREATE_STUB: CreatePostDto = { + ...POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES, + series: SERIES_STUB.name, + tags: ["tags"], +}; + +const GET_POSTS_DTO_STUB: GetPostsDto = { + skip: 0, + limit: 10, + series: "series", + tag: "tag", + text: "text", +}; + +const POST_UPDATE_STUB: UpdatePostDto = { + ...POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES, + _id: "_id", + series: "change_series", + deleteTags: ["delete"], + addTags: ["add"], +}; + +const POST_STUB = { + _id: "_id", + ...POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES, + series: SERIES_STUB, + tags: [TAG_STUB], + likes: ["likes"], + views: ["views"], + nid: 1, +}; + +export { + POST_CREATE_STUB_WITHOUT_TAGS_AND_SERIES, + POST_CREATE_STUB, + POST_UPDATE_STUB, + GET_POSTS_DTO_STUB, + POST_STUB, +}; diff --git a/test/utils/stub/project.ts b/test/utils/stub/project.ts new file mode 100644 index 0000000..fd02fe9 --- /dev/null +++ b/test/utils/stub/project.ts @@ -0,0 +1,24 @@ +import { ProjectDocument } from "@/routes/project/project.schema"; + +const CREATE_PROJECT_STUB = { + title: "title", + description: "description", + content: "content", + thumbnail: "thumbnail", + github: "github", + page: "page", + start: "2021-01-01", + end: "2021-01-01", +}; + +const UPDATE_PROJECT_STUB = { + _id: "id", + ...CREATE_PROJECT_STUB, +}; + +const PROJECT_STUB = { + ...UPDATE_PROJECT_STUB, + nid: 1, +} as unknown as ProjectDocument; + +export { CREATE_PROJECT_STUB, UPDATE_PROJECT_STUB, PROJECT_STUB }; diff --git a/test/utils/stub/series.ts b/test/utils/stub/series.ts new file mode 100644 index 0000000..77c08aa --- /dev/null +++ b/test/utils/stub/series.ts @@ -0,0 +1,27 @@ +const SERIES_CREATE_INPUT_STUB = { + name: "title", + thumbnail: "thumbnail", +}; + +const SERIES_UPDATE_INPUT_STUB = { + ...SERIES_CREATE_INPUT_STUB, +}; + +const SERIES_STUB_WITHOUT_POSTS = { + ...SERIES_UPDATE_INPUT_STUB, + posts: [], +}; + +const SERIES_STUB = { + ...SERIES_STUB_WITHOUT_POSTS, + _id: "id", + posts: [{ _id: "id" }], + nid: 1, +}; + +export { + SERIES_CREATE_INPUT_STUB, + SERIES_UPDATE_INPUT_STUB, + SERIES_STUB_WITHOUT_POSTS, + SERIES_STUB, +}; diff --git a/test/utils/stub/skill.ts b/test/utils/stub/skill.ts new file mode 100644 index 0000000..af8e3fc --- /dev/null +++ b/test/utils/stub/skill.ts @@ -0,0 +1,28 @@ +import { CreateSkillDto } from "@/routes/skill/dto/create-skill.dto"; +import { UpdateSkillDto } from "@/routes/skill/dto/update-skill.dto"; +import { SkillDocument } from "@/routes/skill/skill.schema"; +import { SkillType, TFilteredSkills } from "@/types"; + +const CREATE_SKILL_STUB: CreateSkillDto = { + name: "test", + description: "test", + type: SkillType.FRONT_END, + icon: "test", +}; + +const UPDATE_SKILL_STUB: UpdateSkillDto = { + ...CREATE_SKILL_STUB, +}; + +const SKILL_STUB = { + _id: "test", + ...CREATE_SKILL_STUB, +} as unknown as SkillDocument; + +const SKILLS_STUB_BY_SKILL_TYPE = { + [SkillType.FRONT_END]: [SKILL_STUB], + [SkillType.BACK_END]: [SKILL_STUB], + [SkillType.DEV_OPS]: [SKILL_STUB], +} as unknown as TFilteredSkills; + +export { CREATE_SKILL_STUB, UPDATE_SKILL_STUB, SKILL_STUB, SKILLS_STUB_BY_SKILL_TYPE }; diff --git a/test/utils/stub/tag.ts b/test/utils/stub/tag.ts new file mode 100644 index 0000000..3170ea4 --- /dev/null +++ b/test/utils/stub/tag.ts @@ -0,0 +1,17 @@ +const TAG_INPUT_STUB = { + name: "태그", +}; + +const TAG_STUB = { + ...TAG_INPUT_STUB, + _id: "_id", + nid: 1, + posts: [{ _id: "post_id" }], +}; + +const TAG_STUB_WITHOUT_POSTS = { + ...TAG_STUB, + posts: [], +}; + +export { TAG_INPUT_STUB, TAG_STUB, TAG_STUB_WITHOUT_POSTS }; diff --git a/test/utils/stub/user.ts b/test/utils/stub/user.ts new file mode 100644 index 0000000..2211d06 --- /dev/null +++ b/test/utils/stub/user.ts @@ -0,0 +1,38 @@ +import { TOKEN_STUB } from "test/utils/stub"; + +import { UserDocument } from "@/routes/user/user.schema"; + +const TOKEN_USER_STUB = { + _id: "id", +}; + +const USER_ID_PASSWORD_STUB = { + userId: "test", + password: "test", +}; + +const USER_STUB_NON_PASSWORD = { + ...TOKEN_USER_STUB, + userId: "test", + username: "test", + refreshToken: TOKEN_STUB, + nid: 1, +} as unknown as UserDocument; + +const USER_STUB = { + ...USER_STUB_NON_PASSWORD, + password: "test", +} as unknown as UserDocument; + +const USER_INPUT_STUB = { + ...USER_ID_PASSWORD_STUB, + username: "test", +}; + +export { + TOKEN_USER_STUB, + USER_ID_PASSWORD_STUB, + USER_STUB_NON_PASSWORD, + USER_STUB, + USER_INPUT_STUB, +}; diff --git a/tsconfig.paths.json b/tsconfig.paths.json index 2c8ee2b..af71833 100644 --- a/tsconfig.paths.json +++ b/tsconfig.paths.json @@ -2,7 +2,8 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": ["src/*"] + "@/*": ["src/*"], + "test/*": ["test/*"] } } } diff --git a/yarn.lock b/yarn.lock index cc3b08c..ed24cf4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -100,6 +100,525 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 9fdb3e837fc54119b017ea34fd0a6d71d2c88075d99e1e818a5158e0ad30ced67ddbcc423a11ceeef6cc465ab5ffd91830acab516470b48237ca7abd51be9642 + languageName: node + linkType: hard + +"@aws-crypto/ie11-detection@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/ie11-detection@npm:3.0.0" + dependencies: + tslib: ^1.11.1 + checksum: 299b2ddd46eddac1f2d54d91386ceb37af81aef8a800669281c73d634ed17fd855dcfb8b3157f2879344b93a2666a6d602550eb84b71e4d7868100ad6da8f803 + languageName: node + linkType: hard + +"@aws-crypto/sha256-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": ^3.0.0 + "@aws-crypto/sha256-js": ^3.0.0 + "@aws-crypto/supports-web-crypto": ^3.0.0 + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: ca89456bf508db2e08060a7f656460db97ac9a15b11e39d6fa7665e2b156508a1758695bff8e82d0a00178d6ac5c36f35eb4bcfac2e48621265224ca14a19bd2 + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:3.0.0, @aws-crypto/sha256-js@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-js@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 644ded32ea310237811afae873d3c7320739cb6f6cc39dced9c94801379e68e5ee2cca0c34f0384793fa9e750a7e0a5e2468f95754bd08e6fd72ab833c8fe23c + languageName: node + linkType: hard + +"@aws-crypto/supports-web-crypto@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/supports-web-crypto@npm:3.0.0" + dependencies: + tslib: ^1.11.1 + checksum: 35479a1558db9e9a521df6877a99f95670e972c602f2a0349303477e5d638a5baf569fb037c853710e382086e6fd77e8ed58d3fb9b49f6e1186a9d26ce7be006 + languageName: node + linkType: hard + +"@aws-crypto/util@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/util@npm:3.0.0" + dependencies: + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: d29d5545048721aae3d60b236708535059733019a105f8a64b4e4a8eab7cf8dde1546dc56bff7de20d36140a4d1f0f4693e639c5732a7059273a7b1e56354776 + languageName: node + linkType: hard + +"@aws-sdk/client-cognito-identity@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.370.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.370.0 + "@aws-sdk/credential-provider-node": 3.370.0 + "@aws-sdk/middleware-host-header": 3.370.0 + "@aws-sdk/middleware-logger": 3.370.0 + "@aws-sdk/middleware-recursion-detection": 3.370.0 + "@aws-sdk/middleware-signing": 3.370.0 + "@aws-sdk/middleware-user-agent": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@aws-sdk/util-endpoints": 3.370.0 + "@aws-sdk/util-user-agent-browser": 3.370.0 + "@aws-sdk/util-user-agent-node": 3.370.0 + "@smithy/config-resolver": ^1.0.1 + "@smithy/fetch-http-handler": ^1.0.1 + "@smithy/hash-node": ^1.0.1 + "@smithy/invalid-dependency": ^1.0.1 + "@smithy/middleware-content-length": ^1.0.1 + "@smithy/middleware-endpoint": ^1.0.2 + "@smithy/middleware-retry": ^1.0.3 + "@smithy/middleware-serde": ^1.0.1 + "@smithy/middleware-stack": ^1.0.1 + "@smithy/node-config-provider": ^1.0.1 + "@smithy/node-http-handler": ^1.0.2 + "@smithy/protocol-http": ^1.1.0 + "@smithy/smithy-client": ^1.0.3 + "@smithy/types": ^1.1.0 + "@smithy/url-parser": ^1.0.1 + "@smithy/util-base64": ^1.0.1 + "@smithy/util-body-length-browser": ^1.0.1 + "@smithy/util-body-length-node": ^1.0.1 + "@smithy/util-defaults-mode-browser": ^1.0.1 + "@smithy/util-defaults-mode-node": ^1.0.1 + "@smithy/util-retry": ^1.0.3 + "@smithy/util-utf8": ^1.0.1 + tslib: ^2.5.0 + checksum: e13eb18f70ce89ea1d2a111ad74892bbf5ca57a926d97a8787e6fa71e502eed94660cc2e364223151a4c6d019a4d30fd7f21da093f39893e6719ee481fdaf47a + languageName: node + linkType: hard + +"@aws-sdk/client-sso-oidc@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.370.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/middleware-host-header": 3.370.0 + "@aws-sdk/middleware-logger": 3.370.0 + "@aws-sdk/middleware-recursion-detection": 3.370.0 + "@aws-sdk/middleware-user-agent": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@aws-sdk/util-endpoints": 3.370.0 + "@aws-sdk/util-user-agent-browser": 3.370.0 + "@aws-sdk/util-user-agent-node": 3.370.0 + "@smithy/config-resolver": ^1.0.1 + "@smithy/fetch-http-handler": ^1.0.1 + "@smithy/hash-node": ^1.0.1 + "@smithy/invalid-dependency": ^1.0.1 + "@smithy/middleware-content-length": ^1.0.1 + "@smithy/middleware-endpoint": ^1.0.2 + "@smithy/middleware-retry": ^1.0.3 + "@smithy/middleware-serde": ^1.0.1 + "@smithy/middleware-stack": ^1.0.1 + "@smithy/node-config-provider": ^1.0.1 + "@smithy/node-http-handler": ^1.0.2 + "@smithy/protocol-http": ^1.1.0 + "@smithy/smithy-client": ^1.0.3 + "@smithy/types": ^1.1.0 + "@smithy/url-parser": ^1.0.1 + "@smithy/util-base64": ^1.0.1 + "@smithy/util-body-length-browser": ^1.0.1 + "@smithy/util-body-length-node": ^1.0.1 + "@smithy/util-defaults-mode-browser": ^1.0.1 + "@smithy/util-defaults-mode-node": ^1.0.1 + "@smithy/util-retry": ^1.0.3 + "@smithy/util-utf8": ^1.0.1 + tslib: ^2.5.0 + checksum: a94d58fdef83615fa63ecde638a3ece82c2eaf05d7309a4ab336e0c6bebf8501aad123efbbf0714db4710208897594e13bfe1e450a657aa4bd82fbea338c0946 + languageName: node + linkType: hard + +"@aws-sdk/client-sso@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/client-sso@npm:3.370.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/middleware-host-header": 3.370.0 + "@aws-sdk/middleware-logger": 3.370.0 + "@aws-sdk/middleware-recursion-detection": 3.370.0 + "@aws-sdk/middleware-user-agent": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@aws-sdk/util-endpoints": 3.370.0 + "@aws-sdk/util-user-agent-browser": 3.370.0 + "@aws-sdk/util-user-agent-node": 3.370.0 + "@smithy/config-resolver": ^1.0.1 + "@smithy/fetch-http-handler": ^1.0.1 + "@smithy/hash-node": ^1.0.1 + "@smithy/invalid-dependency": ^1.0.1 + "@smithy/middleware-content-length": ^1.0.1 + "@smithy/middleware-endpoint": ^1.0.2 + "@smithy/middleware-retry": ^1.0.3 + "@smithy/middleware-serde": ^1.0.1 + "@smithy/middleware-stack": ^1.0.1 + "@smithy/node-config-provider": ^1.0.1 + "@smithy/node-http-handler": ^1.0.2 + "@smithy/protocol-http": ^1.1.0 + "@smithy/smithy-client": ^1.0.3 + "@smithy/types": ^1.1.0 + "@smithy/url-parser": ^1.0.1 + "@smithy/util-base64": ^1.0.1 + "@smithy/util-body-length-browser": ^1.0.1 + "@smithy/util-body-length-node": ^1.0.1 + "@smithy/util-defaults-mode-browser": ^1.0.1 + "@smithy/util-defaults-mode-node": ^1.0.1 + "@smithy/util-retry": ^1.0.3 + "@smithy/util-utf8": ^1.0.1 + tslib: ^2.5.0 + checksum: e6797cac371b7da2b01885f0a4bcb81033af2246bb45ca6454d76b223ab23a824667479e9e7a77fe3d99164fb009a970ff6498d9d13ef57587ef1652f0f7f036 + languageName: node + linkType: hard + +"@aws-sdk/client-sts@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/client-sts@npm:3.370.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/credential-provider-node": 3.370.0 + "@aws-sdk/middleware-host-header": 3.370.0 + "@aws-sdk/middleware-logger": 3.370.0 + "@aws-sdk/middleware-recursion-detection": 3.370.0 + "@aws-sdk/middleware-sdk-sts": 3.370.0 + "@aws-sdk/middleware-signing": 3.370.0 + "@aws-sdk/middleware-user-agent": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@aws-sdk/util-endpoints": 3.370.0 + "@aws-sdk/util-user-agent-browser": 3.370.0 + "@aws-sdk/util-user-agent-node": 3.370.0 + "@smithy/config-resolver": ^1.0.1 + "@smithy/fetch-http-handler": ^1.0.1 + "@smithy/hash-node": ^1.0.1 + "@smithy/invalid-dependency": ^1.0.1 + "@smithy/middleware-content-length": ^1.0.1 + "@smithy/middleware-endpoint": ^1.0.2 + "@smithy/middleware-retry": ^1.0.3 + "@smithy/middleware-serde": ^1.0.1 + "@smithy/middleware-stack": ^1.0.1 + "@smithy/node-config-provider": ^1.0.1 + "@smithy/node-http-handler": ^1.0.2 + "@smithy/protocol-http": ^1.1.0 + "@smithy/smithy-client": ^1.0.3 + "@smithy/types": ^1.1.0 + "@smithy/url-parser": ^1.0.1 + "@smithy/util-base64": ^1.0.1 + "@smithy/util-body-length-browser": ^1.0.1 + "@smithy/util-body-length-node": ^1.0.1 + "@smithy/util-defaults-mode-browser": ^1.0.1 + "@smithy/util-defaults-mode-node": ^1.0.1 + "@smithy/util-retry": ^1.0.3 + "@smithy/util-utf8": ^1.0.1 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: 55ce8a7a8a8a44cffdbfca10ac2e4d389a4a13e81a4558ef401c3174727296c47375cec5898023e082285aaf5804d62f5d887da53e343f24aec8d8b5bc4ea29b + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-cognito-identity@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.370.0" + dependencies: + "@aws-sdk/client-cognito-identity": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 3ee862b115b3549bebeff65b6d02e26154f22dc2f9efde8e805c7305537a9cd4b9af7f3a8cb3f7f325c2ae211cb687ca4a9e345990c13319e4e3fad0b33f0503 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-env@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 0295278ca333b8548c417d164569df737a5c587d7d39c35e9d0c9b55022a3d8caa3de04dd242135069cb3570cd9f52a2c1012fcaef4a13f49e91cec6aa0ed9b6 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.370.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.370.0 + "@aws-sdk/credential-provider-process": 3.370.0 + "@aws-sdk/credential-provider-sso": 3.370.0 + "@aws-sdk/credential-provider-web-identity": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/credential-provider-imds": ^1.0.1 + "@smithy/property-provider": ^1.0.1 + "@smithy/shared-ini-file-loader": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 8d8d0d386eb521047ac515fe8bb99faad0b5613dfd1e1824ebc0b427f68d420a68c97ba5b1b5e64a16e3edda3633d76d2f632acdf2c8b37ff39148c8e50c7200 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.370.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.370.0 + "@aws-sdk/credential-provider-ini": 3.370.0 + "@aws-sdk/credential-provider-process": 3.370.0 + "@aws-sdk/credential-provider-sso": 3.370.0 + "@aws-sdk/credential-provider-web-identity": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/credential-provider-imds": ^1.0.1 + "@smithy/property-provider": ^1.0.1 + "@smithy/shared-ini-file-loader": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 564422e77d791fcc307f2954ebf2e7a17273dff63812ed8528196f3f38163db726a71cc1c22d2e6de287727d3506764dab8c9c99de7fe3426d6cec0bf4d4f0ab + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-process@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/shared-ini-file-loader": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 286080cfdb0a433a619cafb29316cc4259022de5369a5a2eefd638a8b025626eb730a6d676bdff4ef0fb905c44fe4a1d4c856f62318553f62b425eea406229de + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-sso@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.370.0" + dependencies: + "@aws-sdk/client-sso": 3.370.0 + "@aws-sdk/token-providers": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/shared-ini-file-loader": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: a5ef52fd213f567ba7aaa243bf4494a4df272522de4db952f0f9a0b1c148920fc49aff43321ec11b08ddd62e34c2686b0d50ae3a20c229e680d392b3c194b0f5 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-web-identity@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 3e9ce1c7749e60302f966a64635a11dece3da47ece8da6e4eae0c784bdf481bee48a0b1085014b11b812ee608fe99fb20548203bfa8d2f43f709b39b1a484f9b + languageName: node + linkType: hard + +"@aws-sdk/credential-providers@npm:^3.186.0": + version: 3.370.0 + resolution: "@aws-sdk/credential-providers@npm:3.370.0" + dependencies: + "@aws-sdk/client-cognito-identity": 3.370.0 + "@aws-sdk/client-sso": 3.370.0 + "@aws-sdk/client-sts": 3.370.0 + "@aws-sdk/credential-provider-cognito-identity": 3.370.0 + "@aws-sdk/credential-provider-env": 3.370.0 + "@aws-sdk/credential-provider-ini": 3.370.0 + "@aws-sdk/credential-provider-node": 3.370.0 + "@aws-sdk/credential-provider-process": 3.370.0 + "@aws-sdk/credential-provider-sso": 3.370.0 + "@aws-sdk/credential-provider-web-identity": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/credential-provider-imds": ^1.0.1 + "@smithy/property-provider": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 7eb60bda6bb4fe22c487aa19469858ba593c49fdad9cc8c2447307cdfdd883fd87743a398007f62ddf7e354eff8dfa716ef614adaaee2712f1a9866f478c32de + languageName: node + linkType: hard + +"@aws-sdk/middleware-host-header@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/protocol-http": ^1.1.0 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 2a7d3f3a0ab75d3c09a9cecc3e545706df2ba561851cde0992a41d00bad2695e5d5f29ff388037a11fb52770b3b9b2a98166cab5c4d1a2fa438ecea32e14604b + languageName: node + linkType: hard + +"@aws-sdk/middleware-logger@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/middleware-logger@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: f3c4062247d2a0064f82e412f027c2cd950d512d9e4a14d603a925ae97cd527e304439e854fb0b2de1f84b429ace317edaa09a8ee9319efec8df47aa3abbb65b + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/protocol-http": ^1.1.0 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: ee259519548171ef3381f9bd48568d8052a7980cfe1a8070d72b44af0522a55baa0e440db5c1460123dc48fb217251a31aea236beda7220af60ebbe2375c96a0 + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-sts@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.370.0" + dependencies: + "@aws-sdk/middleware-signing": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 14877966d56518895d55b30af45ff695b835ced56e46986b3aea12b3f8df953741a18ad24fcd567eae7a4b0c4c501fe4c0a20c01d6604d0e67c2be6435b15037 + languageName: node + linkType: hard + +"@aws-sdk/middleware-signing@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/middleware-signing@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/protocol-http": ^1.1.0 + "@smithy/signature-v4": ^1.0.1 + "@smithy/types": ^1.1.0 + "@smithy/util-middleware": ^1.0.1 + tslib: ^2.5.0 + checksum: f8581ad377a8eea2345b2881adfe0c4fc35a9825ca0b55facbac5f89e088acd8e231d87ae632dd249df71f8b0113046b7cdd8a0b82c69195faf799813da14e91 + languageName: node + linkType: hard + +"@aws-sdk/middleware-user-agent@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@aws-sdk/util-endpoints": 3.370.0 + "@smithy/protocol-http": ^1.1.0 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: c4366db10a7eece54c9f5429352329e7ea31c91a2c05b5b484304fb2e69f07d297e04c387c22ddc11759945e2adc97e6541302391ad8faa5a3aae8e6a1605589 + languageName: node + linkType: hard + +"@aws-sdk/token-providers@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/token-providers@npm:3.370.0" + dependencies: + "@aws-sdk/client-sso-oidc": 3.370.0 + "@aws-sdk/types": 3.370.0 + "@smithy/property-provider": ^1.0.1 + "@smithy/shared-ini-file-loader": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 7126c5bdc86d8a0cefbf92649927747928fd1af76ca943ec8e9c558d99c7550535b694149601c16b096ff429178e7bea51ac2ffc121a24637ddc8b03976759ed + languageName: node + linkType: hard + +"@aws-sdk/types@npm:3.370.0, @aws-sdk/types@npm:^3.222.0": + version: 3.370.0 + resolution: "@aws-sdk/types@npm:3.370.0" + dependencies: + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + checksum: 105a5768f20075035c2250de69f782ea4219c9ed8cd426c9ab57605616c8b1d534764d3c5b29e9715eb68a0e3f99b27ed463c410a3d728abf3c4ad59347e9f4e + languageName: node + linkType: hard + +"@aws-sdk/util-endpoints@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/util-endpoints@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + tslib: ^2.5.0 + checksum: d351ad2fdc92bec16d0d925dbcfc3f38baed12a7984d70bf02ee2dfe4c3fd1a85f53e515f9751f31d1345fe0bdebd62be7cd08e405a31354db92312f0cc6282c + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.310.0 + resolution: "@aws-sdk/util-locate-window@npm:3.310.0" + dependencies: + tslib: ^2.5.0 + checksum: d552ce5f0f836ecb13d7920ae650552c56706f26a5e8abf894ba471e18775a3791869bda95269153735bac9d211efc3ba78ea01c34428c3fed4318ac693a08bc + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-browser@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/types": ^1.1.0 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: 3a549b1337490aaeacc21c3bff0e7459f51e764c6c9a69aafeb7950f54e118d2eeb64b652afb97e2f0aa6777df1e65fced2b96bfcdd98aacc753acfce7847b59 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.370.0": + version: 3.370.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.370.0" + dependencies: + "@aws-sdk/types": 3.370.0 + "@smithy/node-config-provider": ^1.0.1 + "@smithy/types": ^1.1.0 + tslib: ^2.5.0 + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 83b1f2c7f25f44b6bd3aed8e7b9fe3a68e077f3b8b3832648a38eb5985b95d0e5ac469242e1d21aa37e29a6c8b4f9cb2a1d60ea1b6e4a76c32225e58f98da504 + languageName: node + linkType: hard + +"@aws-sdk/util-utf8-browser@npm:^3.0.0": + version: 3.259.0 + resolution: "@aws-sdk/util-utf8-browser@npm:3.259.0" + dependencies: + tslib: ^2.3.1 + checksum: b6a1e580da1c9b62c749814182a7649a748ca4253edb4063aa521df97d25b76eae3359eb1680b86f71aac668e05cc05c514379bca39ebf4ba998ae4348412da8 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.22.5": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" @@ -924,6 +1443,18 @@ __metadata: languageName: node linkType: hard +"@nestjs/axios@npm:^3.0.0": + version: 3.0.0 + resolution: "@nestjs/axios@npm:3.0.0" + peerDependencies: + "@nestjs/common": ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + axios: ^1.3.1 + reflect-metadata: ^0.1.12 + rxjs: ^6.0.0 || ^7.0.0 + checksum: 0483ef5a10ed920eb84756472de59b5bd83d09f152de9b769f68a57bd9c2c922961545c50001ac3ea45584cf991aee355b8fef4fe249b849fded4f1df92244f6 + languageName: node + linkType: hard + "@nestjs/cli@npm:^10.0.0": version: 10.1.8 resolution: "@nestjs/cli@npm:10.1.8" @@ -985,6 +1516,21 @@ __metadata: languageName: node linkType: hard +"@nestjs/config@npm:^3.0.0": + version: 3.0.0 + resolution: "@nestjs/config@npm:3.0.0" + dependencies: + dotenv: 16.1.4 + dotenv-expand: 10.0.0 + lodash: 4.17.21 + uuid: 9.0.0 + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 + checksum: e886dc337cd0fa2018aff5088c5ec9f38331aaf47dbd065ff72e6429a14b213c83cb5a9782b5044c3625a89ef4d7bb426b173773b9d09140433820885731ffa0 + languageName: node + linkType: hard + "@nestjs/core@npm:^10.0.0": version: 10.0.5 resolution: "@nestjs/core@npm:10.0.5" @@ -1013,6 +1559,58 @@ __metadata: languageName: node linkType: hard +"@nestjs/jwt@npm:^10.1.0": + version: 10.1.0 + resolution: "@nestjs/jwt@npm:10.1.0" + dependencies: + "@types/jsonwebtoken": 9.0.2 + jsonwebtoken: 9.0.0 + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + checksum: c172b11ce154f5f98b27a3b3a70a0dadbcad97624e201ccb487b6cf05a8d42edd9ecb8a442e375b974a7c889889f022ab9f684e4ec1019c91b43a8fe4d2d9ea8 + languageName: node + linkType: hard + +"@nestjs/mapped-types@npm:^2.0.2": + version: 2.0.2 + resolution: "@nestjs/mapped-types@npm:2.0.2" + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + checksum: 1fb0dca3838d0dc23107233e95a711e251bfbef9017589d3764c955a1cf190c8372179cee70e3674807896f53a98ab2ba80cd769b4f072fadefb708be1cc911b + languageName: node + linkType: hard + +"@nestjs/mongoose@npm:^10.0.0": + version: 10.0.0 + resolution: "@nestjs/mongoose@npm:10.0.0" + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + "@nestjs/core": ^8.0.0 || ^9.0.0 || ^10.0.0 + mongoose: ^6.0.2 || ^7.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.0.0 + checksum: faf75fdc240554302a44e34f9f23c853c744da54ce55df461aeb81174afc770d4f33f72b14a607041399d2d668e0c8e22fa8fd8f4b2ef9b600096a23b74f6630 + languageName: node + linkType: hard + +"@nestjs/passport@npm:^10.0.0": + version: 10.0.0 + resolution: "@nestjs/passport@npm:10.0.0" + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + passport: ^0.4.0 || ^0.5.0 || ^0.6.0 + checksum: 54436958322435bcdb03bf57cc4c1303f8fbbae56d04a05335f7fd9f1563fdeab45107b2a5ed75732f9596e251b0755d282624301b7ffce7dd9f3af5323b6621 + languageName: node + linkType: hard + "@nestjs/platform-express@npm:^10.0.0": version: 10.0.5 resolution: "@nestjs/platform-express@npm:10.0.5" @@ -1045,8 +1643,8 @@ __metadata: linkType: hard "@nestjs/testing@npm:^10.0.0": - version: 10.0.5 - resolution: "@nestjs/testing@npm:10.0.5" + version: 10.1.0 + resolution: "@nestjs/testing@npm:10.1.0" dependencies: tslib: 2.6.0 peerDependencies: @@ -1059,7 +1657,7 @@ __metadata: optional: true "@nestjs/platform-express": optional: true - checksum: dfc6b492ca5abaf99ec2ccc5e508d95e5149380a77a0b41e946fc3293a33d67dea67a0248db2f624c7a5a201d3929635fcc6905f59986a1e4530896574d447d4 + checksum: 80e609e4d072ec9aa3a66ca22933e3d2303ecb3a9bf97db1b81a8f700d3303e6cba2769b701a237793488edf4c102fb2c4828ff9431129008ecec492ca95df34 languageName: node linkType: hard @@ -1158,6 +1756,422 @@ __metadata: languageName: node linkType: hard +"@smithy/abort-controller@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/abort-controller@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 5d0c5b04636c1e27c812b3ac0f780602ff28ed60dbd5b9d39e9caa6f2e982b340efd3fea4d7e7b282451d1469ef6f9d5086e79a0945a078db2db7672dd174f49 + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^1.0.1, @smithy/config-resolver@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/config-resolver@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + "@smithy/util-config-provider": ^1.0.2 + "@smithy/util-middleware": ^1.0.2 + tslib: ^2.5.0 + checksum: 23efcb59d682b7ab05ec920c92d9e7fcecc58b9ab204a4de504da18ef0687c051b27acf9bcfc91969a160ea137d6f2f2091b4f3e31c659327784f95240ec0d13 + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^1.0.1, @smithy/credential-provider-imds@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/credential-provider-imds@npm:1.0.2" + dependencies: + "@smithy/node-config-provider": ^1.0.2 + "@smithy/property-provider": ^1.0.2 + "@smithy/types": ^1.1.1 + "@smithy/url-parser": ^1.0.2 + tslib: ^2.5.0 + checksum: 91b57891b68fd1e83b40d8bde72cbc38247982ba6eaaf32a80bdcccb1e30b42799ac7d8b50da55796ae873a95891b567298d5da41e1059150e240d075a82ee03 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/eventstream-codec@npm:1.0.2" + dependencies: + "@aws-crypto/crc32": 3.0.0 + "@smithy/types": ^1.1.1 + "@smithy/util-hex-encoding": ^1.0.2 + tslib: ^2.5.0 + checksum: 64e2f6e5b03595815a2b968938459db3df31498b2befa657b5acc55242c57da56825431465bac271344cecf68d1f31a54ed3477ebd7d775ad3791d7e48a2bc62 + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^1.0.1, @smithy/fetch-http-handler@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/fetch-http-handler@npm:1.0.2" + dependencies: + "@smithy/protocol-http": ^1.1.1 + "@smithy/querystring-builder": ^1.0.2 + "@smithy/types": ^1.1.1 + "@smithy/util-base64": ^1.0.2 + tslib: ^2.5.0 + checksum: 62965f69ebcb7a7a67576d4ac909873080d0da33796f15ecf7b265e7f35e0d43e6d3cdf5edb53b9a51f97a88ee90777dd8a7a84ac8dd914cd3d4bcb1c6611506 + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/hash-node@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + "@smithy/util-buffer-from": ^1.0.2 + "@smithy/util-utf8": ^1.0.2 + tslib: ^2.5.0 + checksum: 018ef3271c60e7c8df07c4b5f77b52010a8a6be53e9da3f7e361cda48a6df6f5a52870a7721110338725e83b88d5ef120c2312334b6618f3dfdf80d4e0725cab + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/invalid-dependency@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 51d4bfc25854789a312d87c9bfe1a919e8d7a96052d4010f411c0cb532673a8a3fff92f2d0cf18ccbd05f618f36bfc7fab61159878559ffdd7096de2bd749916 + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/is-array-buffer@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 811b100b809bc1730ed33231ae22afbce5ccc8d2d5db5c8e86b52db718d738283337a43f777397d1308c24243d0be3e3d862684e9be1dbdfb547a8d8399f0911 + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/middleware-content-length@npm:1.0.2" + dependencies: + "@smithy/protocol-http": ^1.1.1 + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 4b00aa741179152cbdc2aa1229077d0a39196f7ce916a11d121cfa160af380651e031f6a8e59193061bbca0baf5fff5f6010b1a998a6c0b03f228fa862625a8a + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^1.0.2": + version: 1.0.3 + resolution: "@smithy/middleware-endpoint@npm:1.0.3" + dependencies: + "@smithy/middleware-serde": ^1.0.2 + "@smithy/types": ^1.1.1 + "@smithy/url-parser": ^1.0.2 + "@smithy/util-middleware": ^1.0.2 + tslib: ^2.5.0 + checksum: 8081b515955632828cdf6955368e46b5288f996fd6697dacf1d44eced488033f8f9d7a10e036fcebabc7f4e72dfcd69ab8ac76b4c17082b9b8326431441ef1a7 + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^1.0.3": + version: 1.0.4 + resolution: "@smithy/middleware-retry@npm:1.0.4" + dependencies: + "@smithy/protocol-http": ^1.1.1 + "@smithy/service-error-classification": ^1.0.3 + "@smithy/types": ^1.1.1 + "@smithy/util-middleware": ^1.0.2 + "@smithy/util-retry": ^1.0.4 + tslib: ^2.5.0 + uuid: ^8.3.2 + checksum: 3ca5c4abe3cbfba1201a7777c0f41ae9df0985a1f155bab647cd88d86c8101f4de0bc118164a14d4ac8bd41d120d0c0f1b327b3512c341aeb06f60b0a32dc89d + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^1.0.1, @smithy/middleware-serde@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/middleware-serde@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 563045c0ad6fd37548197a3844716e61bf969f4a7b3fbb6c6f0129d4745a0c373029acf069d142a59f1d22778e90f86869cd4ef3f9b297b04a23ef042c6c9c55 + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^1.0.1, @smithy/middleware-stack@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/middleware-stack@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 34794d1e6d8b5fd9be70d5d0137f09d8ec88942d054575e35dbf6407f2040c3d0d667d26ff01132c4c42933785127d12b6406a1802af65a779e44cbf38fc9fe2 + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^1.0.1, @smithy/node-config-provider@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/node-config-provider@npm:1.0.2" + dependencies: + "@smithy/property-provider": ^1.0.2 + "@smithy/shared-ini-file-loader": ^1.0.2 + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 593bd5adf0177851a191849e68760f7ebde943db2613898ecda468b31c47442e5fd380fdd95fe95f912bd15fe48a28dbde3f352b49a8108fc34c630d7eb50843 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^1.0.2, @smithy/node-http-handler@npm:^1.0.3": + version: 1.0.3 + resolution: "@smithy/node-http-handler@npm:1.0.3" + dependencies: + "@smithy/abort-controller": ^1.0.2 + "@smithy/protocol-http": ^1.1.1 + "@smithy/querystring-builder": ^1.0.2 + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 727e1391ccc409b3ed7d7a697bfcd29e4e9fbc81404665719c47c8143832ef8f65ea9f8f5d783ea1930fd56a0d45eaaa6a7a8db76da785ba73f0fd29276fbe11 + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^1.0.1, @smithy/property-provider@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/property-provider@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 55d09de3dab2e5858b45f981c67ec1cf4e8fc28a758b9830fd0c5e04ca6e9d69f31e92c80fa4fba5d8eed4a60b4f95528d41f9a6c63e2ff0aad726d3c3b83636 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^1.1.0, @smithy/protocol-http@npm:^1.1.1": + version: 1.1.1 + resolution: "@smithy/protocol-http@npm:1.1.1" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 6320e8b010d05a123efef19401b3e2fc0d03efa99082ad0d5fd41a3209a05332acb4b5ee21dcfd4764b6576019884602bd09ede0f7508540baf7ddb1f7eb49f5 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/querystring-builder@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + "@smithy/util-uri-escape": ^1.0.2 + tslib: ^2.5.0 + checksum: c0e6807a5b6c8438295ff8ebc11d9e2a28fe1434405688055a9dd4f4d354e49e14a4e459419af23f74aab0a281d58b70d29ae6777a42504a89d6f089b110ee52 + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/querystring-parser@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 348672b1bc4193de23055421756c47619de5a2dea496dcf85a7744e9a8d5b26a4573dd731f9be936f7b1ba564676ecdf6d166638f9961bff497a2c554c97f01c + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^1.0.3": + version: 1.0.3 + resolution: "@smithy/service-error-classification@npm:1.0.3" + checksum: adf840de7865606946a68f55ae4f35e03d96422be4df886309300944243510baf1bd84ef0786bcd8184094e97fafdff66a52b8ba669267391a8d8856093edde0 + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^1.0.1, @smithy/shared-ini-file-loader@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/shared-ini-file-loader@npm:1.0.2" + dependencies: + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: c9ba908a48c12a06bcbb872e2e0581805507efc9beffcdbad39baa09b22da63bbcbf538a95d099666869e080ad5556bfe2fab6d4e977a71163595f3f9c1d7edc + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/signature-v4@npm:1.0.2" + dependencies: + "@smithy/eventstream-codec": ^1.0.2 + "@smithy/is-array-buffer": ^1.0.2 + "@smithy/types": ^1.1.1 + "@smithy/util-hex-encoding": ^1.0.2 + "@smithy/util-middleware": ^1.0.2 + "@smithy/util-uri-escape": ^1.0.2 + "@smithy/util-utf8": ^1.0.2 + tslib: ^2.5.0 + checksum: 3e3072ef956adbb9fe633f2e7c28fc9aaeddb7551c25a1af94214fd5e93617c2b0ede440c1cb61ee6268c502ce0418cf124fb21cb654605292a956ec3061d54a + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^1.0.3": + version: 1.0.4 + resolution: "@smithy/smithy-client@npm:1.0.4" + dependencies: + "@smithy/middleware-stack": ^1.0.2 + "@smithy/types": ^1.1.1 + "@smithy/util-stream": ^1.0.2 + tslib: ^2.5.0 + checksum: 468aa2f28b6addb82861a543a2c1639dcfd40e49d3344237bd56c2d061af5e2bc88972297a2b86de7bf31d7ee49589a4e5a577518ef747e2c7458ae20f7c3322 + languageName: node + linkType: hard + +"@smithy/types@npm:^1.1.0, @smithy/types@npm:^1.1.1": + version: 1.1.1 + resolution: "@smithy/types@npm:1.1.1" + dependencies: + tslib: ^2.5.0 + checksum: bf4b632eb7d668d8b99e99facf514d506868121e24c0adfaaa52f7da4056644fda5cb4324355f1dba16fc43a4c9af9ef7853db2a4895c3fca11ac98cf6d12234 + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^1.0.1, @smithy/url-parser@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/url-parser@npm:1.0.2" + dependencies: + "@smithy/querystring-parser": ^1.0.2 + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 1844b23a30afb689bc9ec21238d9e6cc428e152201f8f30f252f6211ee8b74760d6568e42125b75bc4d110b15476eac22c204c7e36f6970d04e8afa5bc83ec1d + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^1.0.1, @smithy/util-base64@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-base64@npm:1.0.2" + dependencies: + "@smithy/util-buffer-from": ^1.0.2 + tslib: ^2.5.0 + checksum: 618d2952fbbc2cabcc6234c9aad612417a2a6f2fcac556a81b75f59d651e5daea7b2c1b44894b30611a425f32045a2dbe0623ebf57654e2068486d0d5a4b6ac7 + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/util-body-length-browser@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 3b0eacc3761625aaf5c384da8487a38663325385826ca9ac0fee17b7dff879a2cb3564a03d146e0b66ba6c503e1a07f159a9cccd4d7711b7063516acbfadb496 + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/util-body-length-node@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 472371dc0a44a72333111758b8e60428cdca088cfe8a87c98413c81c4083e8988ae9d27f20fa2b0a8aed7fc26c0fec8e13c271494772c619f65f1a9f4e665eb3 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-buffer-from@npm:1.0.2" + dependencies: + "@smithy/is-array-buffer": ^1.0.2 + tslib: ^2.5.0 + checksum: ca4308ebc54f372777fc53b2782c752ebfdfe3934b1c4da935c78bb2f4f921d2071056edc644704ba894d363f85cc52fc07095d93eb6cbba510ba19a767be265 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-config-provider@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 625c100b8af62cb08c167ebd23f77250e62b012e085ec8db8f06e3539fc516c340e14b480a09012192595121a58276dfc4370529237a58393185f3782ed0244a + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/util-defaults-mode-browser@npm:1.0.2" + dependencies: + "@smithy/property-provider": ^1.0.2 + "@smithy/types": ^1.1.1 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: ca03c9f596f9a26d392c289de7af7c7e7bdb12648b0339074bcbaddd762e58cb0cf127b8ed24e269bff004465702986b90d5151c502b418a615449171432024b + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^1.0.1": + version: 1.0.2 + resolution: "@smithy/util-defaults-mode-node@npm:1.0.2" + dependencies: + "@smithy/config-resolver": ^1.0.2 + "@smithy/credential-provider-imds": ^1.0.2 + "@smithy/node-config-provider": ^1.0.2 + "@smithy/property-provider": ^1.0.2 + "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: 805977758a5cabe3b8a4ee69f29cf6861356096239a60269eac6b83a4d11eddba030b0a5bcdb64add3d1301f611b21ed7d773e354cac8ff08b9d85ba47960252 + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-hex-encoding@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: c70bfccb41ac702b514a2bc3d6f897a616efa5aba880a6c7a7501ea874bea3c2213868cb2c494c19f6a5190f3f9e4f7d73d9ab87addbd243435e287dc892dc30 + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^1.0.1, @smithy/util-middleware@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-middleware@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 899aca62efab92ac1b55e801b2bb1a3e5eb1b74f52387abe40f902ec2e5f3afd36e2942f392ff1a562e69dd0de901b9aa20c55e4af2ff0e37e20c14b20940852 + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^1.0.3, @smithy/util-retry@npm:^1.0.4": + version: 1.0.4 + resolution: "@smithy/util-retry@npm:1.0.4" + dependencies: + "@smithy/service-error-classification": ^1.0.3 + tslib: ^2.5.0 + checksum: b9a7e464d67b141a175f126b2a0fc5c91de387eb483b432db4769cee43bbbb546e4142711f293c3a04c82b6d56b676b7a2f7f7150e2c736a10d8e5df1ed63c04 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-stream@npm:1.0.2" + dependencies: + "@smithy/fetch-http-handler": ^1.0.2 + "@smithy/node-http-handler": ^1.0.3 + "@smithy/types": ^1.1.1 + "@smithy/util-base64": ^1.0.2 + "@smithy/util-buffer-from": ^1.0.2 + "@smithy/util-hex-encoding": ^1.0.2 + "@smithy/util-utf8": ^1.0.2 + tslib: ^2.5.0 + checksum: ebea6886766da812434d4ca39ee201302096451441d577eb5efeb44ce3437f3828df435676a5e453f8eb535e19224bc3d21c2d3e6632a5eef68d932a37343476 + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-uri-escape@npm:1.0.2" + dependencies: + tslib: ^2.5.0 + checksum: 6df9fb3aed8fc386c1b7ccb2b52268c2e67f3620a85fa42d35f964ae2c492b6395841a751a0ab4068825c7fd54a84933014571d6fbc12e9e1a711d0be3f2c747 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^1.0.1, @smithy/util-utf8@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-utf8@npm:1.0.2" + dependencies: + "@smithy/util-buffer-from": ^1.0.2 + tslib: ^2.5.0 + checksum: 64add10ac1bbeb0c96a1ba8b05886ae86cee589fd4cccb25276849748fed7035c54e64d40107f22a57ca9b4f9324afe4baf0007c67536d520c1b8378e1e6ec55 + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -1253,6 +2267,15 @@ __metadata: languageName: node linkType: hard +"@types/cookie-parser@npm:^1.4.3": + version: 1.4.3 + resolution: "@types/cookie-parser@npm:1.4.3" + dependencies: + "@types/express": "*" + checksum: f390f3af1b1711190dee2c2ecd9af33af81fbde8d81ee820dadb6fe1e0d80c3faba40af37c6ed36fb88b04b64870f6a021f7e9edceecd17c42fe22abe0af5005 + languageName: node + linkType: hard + "@types/cookiejar@npm:*": version: 2.1.2 resolution: "@types/cookiejar@npm:2.1.2" @@ -1299,7 +2322,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:^4.17.17": +"@types/express@npm:*, @types/express@npm:^4.17.17": version: 4.17.17 resolution: "@types/express@npm:4.17.17" dependencies: @@ -1376,6 +2399,15 @@ __metadata: languageName: node linkType: hard +"@types/jsonwebtoken@npm:9.0.2": + version: 9.0.2 + resolution: "@types/jsonwebtoken@npm:9.0.2" + dependencies: + "@types/node": "*" + checksum: 3bb8d40e78d7eb53e427db6e9f0f22e0890cfee80965dcf741d08341814913afb211306de6e9847c6d241cc8e36f8a59090cbfdcc510ab7c81af9d650c5afe0e + languageName: node + linkType: hard + "@types/mime@npm:*": version: 3.0.1 resolution: "@types/mime@npm:3.0.1" @@ -1390,6 +2422,15 @@ __metadata: languageName: node linkType: hard +"@types/multer@npm:^1.4.7": + version: 1.4.7 + resolution: "@types/multer@npm:1.4.7" + dependencies: + "@types/express": "*" + checksum: 680cb0710aa25264d20cdcdaf34c212b636b55ea141310f06c25354ab1401193c7aa6839f9d22abf64a223fab1f2b8287f2512b0bef7e1628c4e9ffe54b4aeb2 + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:^20.3.1": version: 20.4.2 resolution: "@types/node@npm:20.4.2" @@ -1479,6 +2520,30 @@ __metadata: languageName: node linkType: hard +"@types/validator@npm:^13.7.10": + version: 13.7.17 + resolution: "@types/validator@npm:13.7.17" + checksum: a827e480c09b957fe1e773e15a18e79930c546c21044ca99d9fb832268d85536cd3577391b6a3c6ebdb38eee244439056ebeed0886123e248d6b8be318c8374e + languageName: node + linkType: hard + +"@types/webidl-conversions@npm:*": + version: 7.0.0 + resolution: "@types/webidl-conversions@npm:7.0.0" + checksum: 60142c7ddd9eb6f907d232d6b3a81ecf990f73b5a62a004eba8bd0f54809a42ece68ce512e7e3e1d98af8b6393d66cddb96f3622d2fb223c4e9c8937c61bfed7 + languageName: node + linkType: hard + +"@types/whatwg-url@npm:^8.2.1": + version: 8.2.2 + resolution: "@types/whatwg-url@npm:8.2.2" + dependencies: + "@types/node": "*" + "@types/webidl-conversions": "*" + checksum: 5dc5afe078dfa1a8a266745586fa3db9baa8ce7cc904789211d1dca1d34d7f3dd17d0b7423c36bc9beab9d98aa99338f1fc60798c0af6cbb8356f20e20d9f243 + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -1785,24 +2850,42 @@ __metadata: version: 0.0.0-use.local resolution: "SEOKO-server@workspace:." dependencies: + "@nestjs/axios": ^3.0.0 "@nestjs/cli": ^10.0.0 "@nestjs/common": ^10.0.0 + "@nestjs/config": ^3.0.0 "@nestjs/core": ^10.0.0 + "@nestjs/jwt": ^10.1.0 + "@nestjs/mapped-types": ^2.0.2 + "@nestjs/mongoose": ^10.0.0 + "@nestjs/passport": ^10.0.0 "@nestjs/platform-express": ^10.0.0 "@nestjs/schematics": ^10.0.0 "@nestjs/testing": ^10.0.0 + "@types/cookie-parser": ^1.4.3 "@types/express": ^4.17.17 "@types/jest": ^29.5.2 + "@types/multer": ^1.4.7 "@types/node": ^20.3.1 "@types/supertest": ^2.0.12 "@typescript-eslint/eslint-plugin": ^5.59.11 "@typescript-eslint/parser": ^5.59.11 + bcryptjs: ^2.4.3 + class-transformer: ^0.5.1 + class-validator: ^0.14.0 + cookie-parser: ^1.4.6 + crypto-js: ^4.1.1 eslint: ^8.42.0 eslint-config-prettier: ^8.8.0 eslint-import-resolver-typescript: ^3.5.5 eslint-plugin-import: ^2.27.5 eslint-plugin-prettier: ^4.2.1 jest: ^29.5.0 + mongodb-memory-server: ^8.13.0 + mongoose: ^7.3.4 + multer: ^1.4.5-lts.1 + passport-jwt: ^4.0.1 + passport-local: ^1.0.0 prettier: ^2.8.8 reflect-metadata: ^0.1.13 rxjs: ^7.8.1 @@ -2138,6 +3221,15 @@ __metadata: languageName: node linkType: hard +"async-mutex@npm:^0.3.2": + version: 0.3.2 + resolution: "async-mutex@npm:0.3.2" + dependencies: + tslib: ^2.3.1 + checksum: 620b771dfdea1cad0a6b712915c31a1e3ca880a8cf1eae92b4590f435995e0260929c6ebaae0b9126b1456790ea498064b5bb9a506948cda760f48d3d0dcc4c8 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -2152,6 +3244,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:*": + version: 1.4.0 + resolution: "axios@npm:1.4.0" + dependencies: + follow-redirects: ^1.15.0 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b + languageName: node + linkType: hard + "babel-jest@npm:^29.6.1": version: 29.6.1 resolution: "babel-jest@npm:29.6.1" @@ -2242,6 +3345,13 @@ __metadata: languageName: node linkType: hard +"bcryptjs@npm:^2.4.3": + version: 2.4.3 + resolution: "bcryptjs@npm:2.4.3" + checksum: 0e80ed852a41f5dfb1853f53ee14a7390b0ef263ce05dba6e2ef3cd919dfad025a7c21ebcfe5bc7fa04b100990edf90c7a877ff7fe623d3e479753253131b629 + languageName: node + linkType: hard + "big-integer@npm:^1.6.44": version: 1.6.51 resolution: "big-integer@npm:1.6.51" @@ -2256,7 +3366,7 @@ __metadata: languageName: node linkType: hard -"bl@npm:^4.1.0": +"bl@npm:^4.0.3, bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" dependencies: @@ -2307,6 +3417,13 @@ __metadata: languageName: node linkType: hard +"bowser@npm:^2.11.0": + version: 2.11.0 + resolution: "bowser@npm:2.11.0" + checksum: 29c3f01f22e703fa6644fc3b684307442df4240b6e10f6cfe1b61c6ca5721073189ca97cdeedb376081148c8518e33b1d818a57f781d70b0b70e1f31fb48814f + languageName: node + linkType: hard + "bplist-parser@npm:^0.2.0": version: 0.2.0 resolution: "bplist-parser@npm:0.2.0" @@ -2367,12 +3484,42 @@ __metadata: languageName: node linkType: hard -"bser@npm:2.1.1": - version: 2.1.1 - resolution: "bser@npm:2.1.1" - dependencies: - node-int64: ^0.4.0 - checksum: 9ba4dc58ce86300c862bffc3ae91f00b2a03b01ee07f3564beeeaf82aa243b8b03ba53f123b0b842c190d4399b94697970c8e7cf7b1ea44b61aa28c3526a4449 +"bser@npm:2.1.1": + version: 2.1.1 + resolution: "bser@npm:2.1.1" + dependencies: + node-int64: ^0.4.0 + checksum: 9ba4dc58ce86300c862bffc3ae91f00b2a03b01ee07f3564beeeaf82aa243b8b03ba53f123b0b842c190d4399b94697970c8e7cf7b1ea44b61aa28c3526a4449 + languageName: node + linkType: hard + +"bson@npm:^4.7.2": + version: 4.7.2 + resolution: "bson@npm:4.7.2" + dependencies: + buffer: ^5.6.0 + checksum: f357d12c5679c8eb029a62e410ad40fb862b7b91f0fc12a3399fb3668e14aecaa63205ffeeee48735a01d393171743607dcd527eb8c058b6f2bd294079ee4125 + languageName: node + linkType: hard + +"bson@npm:^5.3.0": + version: 5.4.0 + resolution: "bson@npm:5.4.0" + checksum: 1c07e3d09f139d414bd226bf7f4e9aaa7a726e0c9718c55b53bb23ffa2805cac8b66e4fa46b424c73a35c6e292ed5f7432df5c76ea5d08052642b2ac9e0399e3 + languageName: node + linkType: hard + +"buffer-crc32@npm:~0.2.3": + version: 0.2.13 + resolution: "buffer-crc32@npm:0.2.13" + checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c + languageName: node + linkType: hard + +"buffer-equal-constant-time@npm:1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 80bb945f5d782a56f374b292770901065bad21420e34936ecbe949e57724b4a13874f735850dd1cc61f078773c4fb5493a41391e7bda40d1fa388d6bd80daaab languageName: node linkType: hard @@ -2383,7 +3530,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0": +"buffer@npm:^5.5.0, buffer@npm:^5.6.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -2462,7 +3609,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.2.0": +"camelcase@npm:^6.2.0, camelcase@npm:^6.3.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d @@ -2558,6 +3705,24 @@ __metadata: languageName: node linkType: hard +"class-transformer@npm:^0.5.1": + version: 0.5.1 + resolution: "class-transformer@npm:0.5.1" + checksum: f191c8b4cc4239990f5efdd790cabdd852c243ed66248e39f6616a349c910c6eed2d9fd1fbf7ee6ea89f69b4f1d0b493b27347fe0cd0ae26b47c3745a805b6d3 + languageName: node + linkType: hard + +"class-validator@npm:^0.14.0": + version: 0.14.0 + resolution: "class-validator@npm:0.14.0" + dependencies: + "@types/validator": ^13.7.10 + libphonenumber-js: ^1.10.14 + validator: ^13.7.0 + checksum: f62e4a0ad24cee68f4b2bc70d32b96de90cb598f96bde362b4dbf4234151af8eb6ae225458312a38fc49fa3959844cf61c60e731a8205e9a570454cff8de2710 + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -2710,6 +3875,13 @@ __metadata: languageName: node linkType: hard +"commondir@npm:^1.0.1": + version: 1.0.1 + resolution: "commondir@npm:1.0.1" + checksum: 59715f2fc456a73f68826285718503340b9f0dd89bfffc42749906c5cf3d4277ef11ef1cca0350d0e79204f00f1f6d83851ececc9095dc88512a697ac0b9bdcb + languageName: node + linkType: hard + "component-emitter@npm:^1.3.0": version: 1.3.0 resolution: "component-emitter@npm:1.3.0" @@ -2780,6 +3952,16 @@ __metadata: languageName: node linkType: hard +"cookie-parser@npm:^1.4.6": + version: 1.4.6 + resolution: "cookie-parser@npm:1.4.6" + dependencies: + cookie: 0.4.1 + cookie-signature: 1.0.6 + checksum: 1e5a63aa82e8eb4e02d2977c6902983dee87b02e87ec5ec43ac3cb1e72da354003716570cd5190c0ad9e8a454c9d3237f4ad6e2f16d0902205a96a1c72b77ba5 + languageName: node + linkType: hard + "cookie-signature@npm:1.0.6": version: 1.0.6 resolution: "cookie-signature@npm:1.0.6" @@ -2787,6 +3969,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:0.4.1": + version: 0.4.1 + resolution: "cookie@npm:0.4.1" + checksum: bd7c47f5d94ab70ccdfe8210cde7d725880d2fcda06d8e375afbdd82de0c8d3b73541996e9ce57d35f67f672c4ee6d60208adec06b3c5fc94cebb85196084cf8 + languageName: node + linkType: hard + "cookie@npm:0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" @@ -2849,6 +4038,13 @@ __metadata: languageName: node linkType: hard +"crypto-js@npm:^4.1.1": + version: 4.1.1 + resolution: "crypto-js@npm:4.1.1" + checksum: b3747c12ee3a7632fab3b3e171ea50f78b182545f0714f6d3e7e2858385f0f4101a15f2517e033802ce9d12ba50a391575ff4638c9de3dd9b2c4bc47768d5425 + languageName: node + linkType: hard + "debug@npm:2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -2858,7 +4054,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.x, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -3034,6 +4230,20 @@ __metadata: languageName: node linkType: hard +"dotenv-expand@npm:10.0.0": + version: 10.0.0 + resolution: "dotenv-expand@npm:10.0.0" + checksum: 2a38b470efe0abcb1ac8490421a55e1d764dc9440fd220942bce40965074f3fb00b585f4346020cb0f0f219966ee6b4ee5023458b3e2953fe5b3214de1b314ee + languageName: node + linkType: hard + +"dotenv@npm:16.1.4": + version: 16.1.4 + resolution: "dotenv@npm:16.1.4" + checksum: c1b2e13df4d374a6a29e134c56c7b040ba20500677fe8b9939ea654f3b3badb9aaa0b172e40e4dfa1233a4177dbb8fb79d84cc79a50ac9c9641fe2ad98c14876 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -3041,6 +4251,15 @@ __metadata: languageName: node linkType: hard +"ecdsa-sig-formatter@npm:1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: ^5.0.1 + checksum: 207f9ab1c2669b8e65540bce29506134613dd5f122cccf1e6a560f4d63f2732d427d938f8481df175505aad94583bcb32c688737bb39a6df0625f903d6d93c03 + languageName: node + linkType: hard + "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -3092,7 +4311,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.1.0": +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -3668,6 +4887,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:4.2.5": + version: 4.2.5 + resolution: "fast-xml-parser@npm:4.2.5" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: d32b22005504eeb207249bf40dc82d0994b5bb9ca9dcc731d335a1f425e47fe085b3cace3cf9d32172dd1a5544193c49e8615ca95b4bf95a4a4920a226b06d80 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.15.0 resolution: "fastq@npm:1.15.0" @@ -3686,6 +4916,15 @@ __metadata: languageName: node linkType: hard +"fd-slicer@npm:~1.1.0": + version: 1.1.0 + resolution: "fd-slicer@npm:1.1.0" + dependencies: + pend: ~1.2.0 + checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 + languageName: node + linkType: hard + "figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -3728,6 +4967,17 @@ __metadata: languageName: node linkType: hard +"find-cache-dir@npm:^3.3.2": + version: 3.3.2 + resolution: "find-cache-dir@npm:3.3.2" + dependencies: + commondir: ^1.0.1 + make-dir: ^3.0.2 + pkg-dir: ^4.1.0 + checksum: 1e61c2e64f5c0b1c535bd85939ae73b0e5773142713273818cc0b393ee3555fb0fd44e1a5b161b8b6c3e03e98c2fcc9c227d784850a13a90a8ab576869576817 + languageName: node + linkType: hard + "find-up@npm:^4.0.0, find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" @@ -3765,6 +5015,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.0": + version: 1.15.2 + resolution: "follow-redirects@npm:1.15.2" + peerDependenciesMeta: + debug: + optional: true + checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -3844,6 +5104,13 @@ __metadata: languageName: node linkType: hard +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d + languageName: node + linkType: hard + "fs-extra@npm:^10.0.0": version: 10.1.0 resolution: "fs-extra@npm:10.1.0" @@ -3981,6 +5248,13 @@ __metadata: languageName: node linkType: hard +"get-port@npm:^5.1.1": + version: 5.1.1 + resolution: "get-port@npm:5.1.1" + checksum: 0162663ffe5c09e748cd79d97b74cd70e5a5c84b760a475ce5767b357fb2a57cb821cee412d646aa8a156ed39b78aab88974eddaa9e5ee926173c036c0713787 + languageName: node + linkType: hard + "get-stream@npm:^5.0.0": version: 5.2.0 resolution: "get-stream@npm:5.2.0" @@ -4278,7 +5552,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": +"https-proxy-agent@npm:^5.0.0, https-proxy-agent@npm:^5.0.1": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" dependencies: @@ -5388,6 +6662,58 @@ __metadata: languageName: node linkType: hard +"jsonwebtoken@npm:9.0.0": + version: 9.0.0 + resolution: "jsonwebtoken@npm:9.0.0" + dependencies: + jws: ^3.2.2 + lodash: ^4.17.21 + ms: ^2.1.1 + semver: ^7.3.8 + checksum: b9181cecf9df99f1dc0253f91ba000a1aa4d91f5816d1608c0dba61a5623726a0bfe200b51df25de18c1a6000825d231ad7ce2788aa54fd48dcb760ad9eb9514 + languageName: node + linkType: hard + +"jsonwebtoken@npm:^9.0.0": + version: 9.0.1 + resolution: "jsonwebtoken@npm:9.0.1" + dependencies: + jws: ^3.2.2 + lodash: ^4.17.21 + ms: ^2.1.1 + semver: ^7.3.8 + checksum: 0eafe268896f4e8f9ab1f0f20e8c645721b7a9cddc41c0aba1e58da5c34564e8c9990817c1a5b646d795bcbb1339350826fe57c4569b5379ba9eea4a9aa5bbd0 + languageName: node + linkType: hard + +"jwa@npm:^1.4.1": + version: 1.4.1 + resolution: "jwa@npm:1.4.1" + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: ^5.0.1 + checksum: ff30ea7c2dcc61f3ed2098d868bf89d43701605090c5b21b5544b512843ec6fd9e028381a4dda466cbcdb885c2d1150f7c62e7168394ee07941b4098e1035e2f + languageName: node + linkType: hard + +"jws@npm:^3.2.2": + version: 3.2.2 + resolution: "jws@npm:3.2.2" + dependencies: + jwa: ^1.4.1 + safe-buffer: ^5.0.1 + checksum: f0213fe5b79344c56cd443428d8f65c16bf842dc8cb8f5aed693e1e91d79c20741663ad6eff07a6d2c433d1831acc9814e8d7bada6a0471fbb91d09ceb2bf5c2 + languageName: node + linkType: hard + +"kareem@npm:2.5.1": + version: 2.5.1 + resolution: "kareem@npm:2.5.1" + checksum: b019a960a7b9e44b6ef224ef85e7583d4e969619f53319e571677fbed7e57e01ee8774589726b29741e42790996567d878003c18e674296742dc343bfbf3efb9 + languageName: node + linkType: hard + "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -5412,6 +6738,13 @@ __metadata: languageName: node linkType: hard +"libphonenumber-js@npm:^1.10.14": + version: 1.10.37 + resolution: "libphonenumber-js@npm:1.10.37" + checksum: c28f7623226d6ab33c48044f12e772d35dfdbebae53e89434840f64da92e92736fa547a47b48d99050f225d921df71d9827848f78d6c62a063dec4c44dad1b1b + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -5458,7 +6791,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.21": +"lodash@npm:4.17.21, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -5523,7 +6856,7 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^3.0.0": +"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2": version: 3.1.0 resolution: "make-dir@npm:3.1.0" dependencies: @@ -5571,6 +6904,15 @@ __metadata: languageName: node linkType: hard +"md5-file@npm:^5.0.0": + version: 5.0.0 + resolution: "md5-file@npm:5.0.0" + bin: + md5-file: cli.js + checksum: c606a00ff58adf5428e8e2f36d86e5d3c7029f9688126faca302cd83b5e92cac183a62e1d1f05fae7c2614e80f993326fd0a8d6a3a913c41ec7ea0eefc25aa76 + languageName: node + linkType: hard + "media-typer@npm:0.3.0": version: 0.3.0 resolution: "media-typer@npm:0.3.0" @@ -5587,6 +6929,13 @@ __metadata: languageName: node linkType: hard +"memory-pager@npm:^1.0.2": + version: 1.5.0 + resolution: "memory-pager@npm:1.5.0" + checksum: d1a2e684583ef55c61cd3a49101da645b11ad57014dfc565e0b43baa9004b743f7e4ab81493d8fff2ab24e9950987cc3209c94bcc4fc8d7e30a475489a1f15e9 + languageName: node + linkType: hard + "merge-descriptors@npm:1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" @@ -5818,6 +7167,123 @@ __metadata: languageName: node linkType: hard +"mongodb-connection-string-url@npm:^2.5.4, mongodb-connection-string-url@npm:^2.6.0": + version: 2.6.0 + resolution: "mongodb-connection-string-url@npm:2.6.0" + dependencies: + "@types/whatwg-url": ^8.2.1 + whatwg-url: ^11.0.0 + checksum: 1d662f0ecfe96f7a400f625c244b2e52914c98f3562ee7d19941127578b5f8237624433bdcea285a654041b945b518803512989690c74548aec5860c5541c605 + languageName: node + linkType: hard + +"mongodb-memory-server-core@npm:8.13.0": + version: 8.13.0 + resolution: "mongodb-memory-server-core@npm:8.13.0" + dependencies: + async-mutex: ^0.3.2 + camelcase: ^6.3.0 + debug: ^4.3.4 + find-cache-dir: ^3.3.2 + get-port: ^5.1.1 + https-proxy-agent: ^5.0.1 + md5-file: ^5.0.0 + mongodb: ^4.16.0 + new-find-package-json: ^2.0.0 + semver: ^7.5.1 + tar-stream: ^2.1.4 + tslib: ^2.5.3 + uuid: ^9.0.0 + yauzl: ^2.10.0 + checksum: 05ee432d70acf4779b8d3aee63d76216d3e0fc92ab81f1588ad323d58d6372603b1a6d15dc57c066a3ca7d78c993019f4c337a995d3222ffad25534278119c45 + languageName: node + linkType: hard + +"mongodb-memory-server@npm:^8.13.0": + version: 8.13.0 + resolution: "mongodb-memory-server@npm:8.13.0" + dependencies: + mongodb-memory-server-core: 8.13.0 + tslib: ^2.5.3 + checksum: 93bc1f40dbf7330a009fb9a6ffab84b151a1f5ced3614f22028139447249cb821c638b61d085eb7c4a68d00f4de836bb444e52fcd32e426a890cfe685d3d8211 + languageName: node + linkType: hard + +"mongodb@npm:5.6.0": + version: 5.6.0 + resolution: "mongodb@npm:5.6.0" + dependencies: + bson: ^5.3.0 + mongodb-connection-string-url: ^2.6.0 + saslprep: ^1.0.3 + socks: ^2.7.1 + peerDependencies: + "@aws-sdk/credential-providers": ^3.201.0 + mongodb-client-encryption: ">=2.3.0 <3" + snappy: ^7.2.2 + dependenciesMeta: + saslprep: + optional: true + peerDependenciesMeta: + "@aws-sdk/credential-providers": + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + checksum: 5b1594b24795b2a7824095a917ede2fc37aaef31e60320c833ee9b11cc2f3de9dd87d5afacea2e41eb5eb579ac9780f5bc4b0bb11d3b1be78e8bf0014528192a + languageName: node + linkType: hard + +"mongodb@npm:^4.16.0": + version: 4.16.0 + resolution: "mongodb@npm:4.16.0" + dependencies: + "@aws-sdk/credential-providers": ^3.186.0 + bson: ^4.7.2 + mongodb-connection-string-url: ^2.5.4 + saslprep: ^1.0.3 + socks: ^2.7.1 + dependenciesMeta: + "@aws-sdk/credential-providers": + optional: true + saslprep: + optional: true + checksum: f0b1347739cc362b82b3aabc7e7d4d74bc7a344ed1bbafd6f92681bcab440f6cc618ffa0438d41d2789cb34818f3b09d4c78f517b42160ebae55bf2c96f13953 + languageName: node + linkType: hard + +"mongoose@npm:^7.3.4": + version: 7.3.4 + resolution: "mongoose@npm:7.3.4" + dependencies: + bson: ^5.3.0 + kareem: 2.5.1 + mongodb: 5.6.0 + mpath: 0.9.0 + mquery: 5.0.0 + ms: 2.1.3 + sift: 16.0.1 + checksum: 1ffdb8449c7e151c1057679070b00072fb6f676ffcde5e33ff4e180c672a1ba4e7447c02bc0985e32ae25f795744a67974ded06df5a6124bd2dd0c2785974f5c + languageName: node + linkType: hard + +"mpath@npm:0.9.0": + version: 0.9.0 + resolution: "mpath@npm:0.9.0" + checksum: 1052f1f926db04502440f76164ae16ed53aa41f3ce34e7e64e3ed451b7d91ede295c3b600801c5f9eb862f03d9d59b7aa5aaf690c341fc521bef025d0f5cd773 + languageName: node + linkType: hard + +"mquery@npm:5.0.0": + version: 5.0.0 + resolution: "mquery@npm:5.0.0" + dependencies: + debug: 4.x + checksum: 0617ead71e40e3c38ab74a1e46214d578d654bf7916abd8b3fb2ceb433bb6287adfa0960f041f16e2ac41c5ed5d7ce2268582f0a0075fff2561bcd5a3f40b417 + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -5854,6 +7320,21 @@ __metadata: languageName: node linkType: hard +"multer@npm:^1.4.5-lts.1": + version: 1.4.5-lts.1 + resolution: "multer@npm:1.4.5-lts.1" + dependencies: + 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 + checksum: d6dfa78a6ec592b74890412f8962da8a87a3dcfe20f612e039b735b8e0faa72c735516c447f7de694ee0d981eb0a1b892fb9e2402a0348dc6091d18c38d89ecc + languageName: node + linkType: hard + "mute-stream@npm:0.0.8": version: 0.0.8 resolution: "mute-stream@npm:0.0.8" @@ -5889,6 +7370,15 @@ __metadata: languageName: node linkType: hard +"new-find-package-json@npm:^2.0.0": + version: 2.0.0 + resolution: "new-find-package-json@npm:2.0.0" + dependencies: + debug: ^4.3.4 + checksum: 5488ead794bd506894ddd8f3ac6240615e625ce56241ed6ff41a5ff46bdf495a81881bef6d25a3aa16d25f742e86e5629c2d052cd2f60530db3a85b2b1bd146c + languageName: node + linkType: hard + "node-abort-controller@npm:^3.0.1": version: 3.1.1 resolution: "node-abort-controller@npm:3.1.1" @@ -6222,6 +7712,43 @@ __metadata: languageName: node linkType: hard +"passport-jwt@npm:^4.0.1": + version: 4.0.1 + resolution: "passport-jwt@npm:4.0.1" + dependencies: + jsonwebtoken: ^9.0.0 + passport-strategy: ^1.0.0 + checksum: 0669d5bf8fc18ee57eb39601491c59ac084acfa6c972e0a3289e9d88a3ee6fab9e9fb279f359eee81939fedb66904c4a74423004480b60c54621af0d7b2ee0d5 + languageName: node + linkType: hard + +"passport-local@npm:^1.0.0": + version: 1.0.0 + resolution: "passport-local@npm:1.0.0" + dependencies: + passport-strategy: 1.x.x + checksum: 86dc08b12f05f0ce1bb109780ccb0377eff45bbba70aa9c0d65f092b3ef476d344c0443517db2e9f3e5ec0ca849171ab25fb622164e0f69a677173af42b9bba7 + languageName: node + linkType: hard + +"passport-strategy@npm:1.x.x, passport-strategy@npm:^1.0.0": + version: 1.0.0 + resolution: "passport-strategy@npm:1.0.0" + checksum: 5086693f2508e538dffa55a338c89fe8192fb5f4478c71f80cd5890b8573419a098f4fec88b505374f60bbe9049f6f24b9f3992678612528a3370b4dc73354a2 + languageName: node + linkType: hard + +"passport@npm:*": + version: 0.6.0 + resolution: "passport@npm:0.6.0" + dependencies: + passport-strategy: 1.x.x + pause: 0.0.1 + utils-merge: ^1.0.1 + checksum: ef932ad671d50de34765c7a53cd1e058d8331a82a6df09265a9c6c1168911aee4a7b5215803d0101110ab7f317e096b4954ca7e18fb2c33b9929f0bd17dbe159 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -6288,6 +7815,20 @@ __metadata: languageName: node linkType: hard +"pause@npm:0.0.1": + version: 0.0.1 + resolution: "pause@npm:0.0.1" + checksum: e96ee581b68085e6f2ba5adbcb4d4a41fe88e5b514061e76df2fe1905f0f65f4fe5a843b538e9551122c6b9184ff4be266c2ee0ea4614702f9a3d04466d9f462 + languageName: node + linkType: hard + +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -6309,7 +7850,7 @@ __metadata: languageName: node linkType: hard -"pkg-dir@npm:^4.2.0": +"pkg-dir@npm:^4.1.0, pkg-dir@npm:^4.2.0": version: 4.2.0 resolution: "pkg-dir@npm:4.2.0" dependencies: @@ -6398,6 +7939,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.0 resolution: "pump@npm:3.0.0" @@ -6408,7 +7956,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0": +"punycode@npm:^2.1.0, punycode@npm:^2.1.1": version: 2.3.0 resolution: "punycode@npm:2.3.0" checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 @@ -6509,7 +8057,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -6720,7 +8268,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -6752,6 +8300,15 @@ __metadata: languageName: node linkType: hard +"saslprep@npm:^1.0.3": + version: 1.0.3 + resolution: "saslprep@npm:1.0.3" + dependencies: + sparse-bitfield: ^3.0.3 + checksum: 4fdc0b70fb5e523f977de405e12cca111f1f10dd68a0cfae0ca52c1a7919a94d1556598ba2d35f447655c3b32879846c77f9274c90806f6673248ae3cea6ee43 + languageName: node + linkType: hard + "schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" @@ -6772,7 +8329,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3": +"semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.1, semver@npm:^7.5.3": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -6879,6 +8436,13 @@ __metadata: languageName: node linkType: hard +"sift@npm:16.0.1": + version: 16.0.1 + resolution: "sift@npm:16.0.1" + checksum: 5fe18a517a20c35e0c05238797cc605094a6cb602b08c4661268c415b71a10f1a55ee4cc8728552e390e7cb4683a33bcbd68d7971eb44645cc6211e2f00dd233 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" @@ -6932,7 +8496,7 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.6.2": +"socks@npm:^2.6.2, socks@npm:^2.7.1": version: 2.7.1 resolution: "socks@npm:2.7.1" dependencies: @@ -6976,6 +8540,15 @@ __metadata: languageName: node linkType: hard +"sparse-bitfield@npm:^3.0.3": + version: 3.0.3 + resolution: "sparse-bitfield@npm:3.0.3" + dependencies: + memory-pager: ^1.0.2 + checksum: 174da88dbbcc783d5dbd26921931cc83830280b8055fb05333786ebe6fc015b9601b24972b3d55920dd2d9f5fb120576fbfa2469b08e5222c9cadf3f05210aab + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -7151,6 +8724,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "superagent@npm:^8.0.5": version: 8.0.9 resolution: "superagent@npm:8.0.9" @@ -7237,6 +8817,19 @@ __metadata: languageName: node linkType: hard +"tar-stream@npm:^2.1.4": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: ^4.0.3 + end-of-stream: ^1.4.1 + fs-constants: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^3.1.1 + checksum: 699831a8b97666ef50021c767f84924cfee21c142c2eb0e79c63254e140e6408d6d55a065a2992548e72b06de39237ef2b802b99e3ece93ca3904a37622a66f3 + languageName: node + linkType: hard + "tar@npm:^6.1.11, tar@npm:^6.1.2": version: 6.1.15 resolution: "tar@npm:6.1.15" @@ -7358,6 +8951,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^3.0.0": + version: 3.0.0 + resolution: "tr46@npm:3.0.0" + dependencies: + punycode: ^2.1.1 + checksum: 44c3cc6767fb800490e6e9fd64fd49041aa4e49e1f6a012b34a75de739cc9ed3a6405296072c1df8b6389ae139c5e7c6496f659cfe13a04a4bff3a1422981270 + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -7494,14 +9096,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.0, tslib@npm:^2.1.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0": +"tslib@npm:2.6.0, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.5.0, tslib@npm:^2.5.3, tslib@npm:^2.6.0": version: 2.6.0 resolution: "tslib@npm:2.6.0" checksum: c01066038f950016a18106ddeca4649b4d76caa76ec5a31e2a26e10586a59fceb4ee45e96719bf6c715648e7c14085a81fee5c62f7e9ebee68e77a5396e5538f languageName: node linkType: hard -"tslib@npm:^1.8.1": +"tslib@npm:^1.11.1, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd @@ -7700,13 +9302,31 @@ __metadata: languageName: node linkType: hard -"utils-merge@npm:1.0.1": +"utils-merge@npm:1.0.1, utils-merge@npm:^1.0.1": version: 1.0.1 resolution: "utils-merge@npm:1.0.1" checksum: c81095493225ecfc28add49c106ca4f09cdf56bc66731aa8dabc2edbbccb1e1bfe2de6a115e5c6a380d3ea166d1636410b62ef216bb07b3feb1cfde1d95d5080 languageName: node linkType: hard +"uuid@npm:9.0.0, uuid@npm:^9.0.0": + version: 9.0.0 + resolution: "uuid@npm:9.0.0" + bin: + uuid: dist/bin/uuid + checksum: 8dd2c83c43ddc7e1c71e36b60aea40030a6505139af6bee0f382ebcd1a56f6cd3028f7f06ffb07f8cf6ced320b76aea275284b224b002b289f89fe89c389b028 + languageName: node + linkType: hard + +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -7725,6 +9345,13 @@ __metadata: languageName: node linkType: hard +"validator@npm:^13.7.0": + version: 13.9.0 + resolution: "validator@npm:13.9.0" + checksum: e2c936f041f61faa42bafd17c6faddf939498666cd82e88d733621c286893730b008959f4cb12ab3e236148a4f3805c30b85e3dcf5e0efd8b0cbcd36c02bfc0c + languageName: node + linkType: hard + "vary@npm:^1, vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -7767,6 +9394,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^7.0.0": + version: 7.0.0 + resolution: "webidl-conversions@npm:7.0.0" + checksum: f05588567a2a76428515333eff87200fae6c83c3948a7482ebb109562971e77ef6dc49749afa58abb993391227c5697b3ecca52018793e0cb4620a48f10bd21b + languageName: node + linkType: hard + "webpack-node-externals@npm:3.0.0": version: 3.0.0 resolution: "webpack-node-externals@npm:3.0.0" @@ -7781,7 +9415,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.88.1": +"webpack@npm:*, webpack@npm:5.88.1": version: 5.88.1 resolution: "webpack@npm:5.88.1" dependencies: @@ -7818,6 +9452,16 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^11.0.0": + version: 11.0.0 + resolution: "whatwg-url@npm:11.0.0" + dependencies: + tr46: ^3.0.0 + webidl-conversions: ^7.0.0 + checksum: ed4826aaa57e66bb3488a4b25c9cd476c46ba96052747388b5801f137dd740b73fde91ad207d96baf9f17fbcc80fc1a477ad65181b5eb5fa718d27c69501d7af + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -7980,6 +9624,16 @@ __metadata: languageName: node linkType: hard +"yauzl@npm:^2.10.0": + version: 2.10.0 + resolution: "yauzl@npm:2.10.0" + dependencies: + buffer-crc32: ~0.2.3 + fd-slicer: ~1.1.0 + checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b + languageName: node + linkType: hard + "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1"