From 3da6652ff83fcf69f4e7a0cf5c1840561af50644 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 11 Nov 2023 17:00:10 +0900 Subject: [PATCH 001/226] =?UTF-8?q?chore:=20be=20api=20=EC=84=9C=EB=B2=84,?= =?UTF-8?q?=20=EC=B1=84=EC=A0=90=20=EC=84=9C=EB=B2=84=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=ED=99=98=EA=B2=BD=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/.eslintrc.js | 60 + be/algo-with-me-api/.gitignore | 35 + be/algo-with-me-api/.prettierrc | 8 + be/algo-with-me-api/README.md | 59 + be/algo-with-me-api/nest-cli.json | 8 + be/algo-with-me-api/package.json | 70 + be/algo-with-me-api/pnpm-lock.yaml | 5321 +++++++++++++++++ .../src/app.controller.spec.ts | 23 + be/algo-with-me-api/src/app.controller.ts | 13 + be/algo-with-me-api/src/app.module.ts | 11 + be/algo-with-me-api/src/app.service.ts | 8 + be/algo-with-me-api/src/main.ts | 9 + be/algo-with-me-api/test/app.e2e-spec.ts | 21 + be/algo-with-me-api/test/jest-e2e.json | 9 + be/algo-with-me-api/tsconfig.build.json | 4 + be/algo-with-me-api/tsconfig.json | 21 + be/algo-with-me-score/.eslintrc.js | 60 + be/algo-with-me-score/.gitignore | 35 + be/algo-with-me-score/.prettierrc | 8 + be/algo-with-me-score/README.md | 59 + be/algo-with-me-score/nest-cli.json | 8 + be/algo-with-me-score/package.json | 70 + be/algo-with-me-score/pnpm-lock.yaml | 5321 +++++++++++++++++ .../src/app.controller.spec.ts | 23 + be/algo-with-me-score/src/app.controller.ts | 13 + be/algo-with-me-score/src/app.module.ts | 11 + be/algo-with-me-score/src/app.service.ts | 8 + be/algo-with-me-score/src/main.ts | 9 + be/algo-with-me-score/test/app.e2e-spec.ts | 21 + be/algo-with-me-score/test/jest-e2e.json | 9 + be/algo-with-me-score/tsconfig.build.json | 4 + be/algo-with-me-score/tsconfig.json | 21 + 32 files changed, 11360 insertions(+) create mode 100644 be/algo-with-me-api/.eslintrc.js create mode 100644 be/algo-with-me-api/.gitignore create mode 100644 be/algo-with-me-api/.prettierrc create mode 100644 be/algo-with-me-api/README.md create mode 100644 be/algo-with-me-api/nest-cli.json create mode 100644 be/algo-with-me-api/package.json create mode 100644 be/algo-with-me-api/pnpm-lock.yaml create mode 100644 be/algo-with-me-api/src/app.controller.spec.ts create mode 100644 be/algo-with-me-api/src/app.controller.ts create mode 100644 be/algo-with-me-api/src/app.module.ts create mode 100644 be/algo-with-me-api/src/app.service.ts create mode 100644 be/algo-with-me-api/src/main.ts create mode 100644 be/algo-with-me-api/test/app.e2e-spec.ts create mode 100644 be/algo-with-me-api/test/jest-e2e.json create mode 100644 be/algo-with-me-api/tsconfig.build.json create mode 100644 be/algo-with-me-api/tsconfig.json create mode 100644 be/algo-with-me-score/.eslintrc.js create mode 100644 be/algo-with-me-score/.gitignore create mode 100644 be/algo-with-me-score/.prettierrc create mode 100644 be/algo-with-me-score/README.md create mode 100644 be/algo-with-me-score/nest-cli.json create mode 100644 be/algo-with-me-score/package.json create mode 100644 be/algo-with-me-score/pnpm-lock.yaml create mode 100644 be/algo-with-me-score/src/app.controller.spec.ts create mode 100644 be/algo-with-me-score/src/app.controller.ts create mode 100644 be/algo-with-me-score/src/app.module.ts create mode 100644 be/algo-with-me-score/src/app.service.ts create mode 100644 be/algo-with-me-score/src/main.ts create mode 100644 be/algo-with-me-score/test/app.e2e-spec.ts create mode 100644 be/algo-with-me-score/test/jest-e2e.json create mode 100644 be/algo-with-me-score/tsconfig.build.json create mode 100644 be/algo-with-me-score/tsconfig.json diff --git a/be/algo-with-me-api/.eslintrc.js b/be/algo-with-me-api/.eslintrc.js new file mode 100644 index 0000000..bd54263 --- /dev/null +++ b/be/algo-with-me-api/.eslintrc.js @@ -0,0 +1,60 @@ +module.exports = { + extends: [ + 'plugin:@typescript-eslint/recommended', + // nestjs 스타일 가이드 + 'plugin:nestjs/recommended', + // google 스타일 가이드 + // 'google', + // import sort 관련 설정 + 'plugin:import/recommended', + 'plugin:import/typescript', + // prettier + 'prettier', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin', 'nestjs'], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + // import sort 관련 설정 + 'import/order': [ + 'error', + { + groups: ['external', 'builtin', ['parent', 'sibling'], 'internal'], + pathGroups: [ + { + pattern: 'nest', + group: 'external', + position: 'before', + }, + ], + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + 'newlines-between': 'always', + }, + ], + // + }, + settings: { + // import sort 관련 설정 + 'import/resolver': { + typescript: true, + node: true, + }, + }, +}; diff --git a/be/algo-with-me-api/.gitignore b/be/algo-with-me-api/.gitignore new file mode 100644 index 0000000..22f55ad --- /dev/null +++ b/be/algo-with-me-api/.gitignore @@ -0,0 +1,35 @@ +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json \ No newline at end of file diff --git a/be/algo-with-me-api/.prettierrc b/be/algo-with-me-api/.prettierrc new file mode 100644 index 0000000..5736e29 --- /dev/null +++ b/be/algo-with-me-api/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 100, + "useTabs": false, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "all", + "semi": true +} diff --git a/be/algo-with-me-api/README.md b/be/algo-with-me-api/README.md new file mode 100644 index 0000000..d8e5e32 --- /dev/null +++ b/be/algo-with-me-api/README.md @@ -0,0 +1,59 @@ +
+ +[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 +[circleci-url]: https://circleci.com/gh/nestjs/nest + +A progressive Node.js framework for building efficient and scalable server-side applications.
+ + + +## Description + +알고리즘 대회 서비스 api 서버 + +## Installation + +```bash +$ pnpm install +``` + +## Running the app + +```bash +# development +$ pnpm run start + +# watch mode +$ pnpm run start:dev + +# production mode +$ pnpm run start:prod +``` + +## Test + +```bash +# unit tests +$ pnpm run test + +# e2e tests +$ pnpm run test:e2e + +# test coverage +$ pnpm run test:cov +``` diff --git a/be/algo-with-me-api/nest-cli.json b/be/algo-with-me-api/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/be/algo-with-me-api/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json new file mode 100644 index 0000000..52aa189 --- /dev/null +++ b/be/algo-with-me-api/package.json @@ -0,0 +1,70 @@ +{ + "name": "algo-with-me-api", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@nestjs/cli": "^10.0.0", + "@nestjs/schematics": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/express": "^4.17.17", + "@types/jest": "^29.5.2", + "@types/node": "^20.3.1", + "@types/supertest": "^2.0.12", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-nestjs": "^1.2.3", + "eslint-plugin-prettier": "^5.0.0", + "jest": "^29.5.0", + "prettier": "^3.0.0", + "source-map-support": "^0.5.21", + "supertest": "^6.3.3", + "ts-jest": "^29.1.0", + "ts-loader": "^9.4.3", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.1.3" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml new file mode 100644 index 0000000..103b829 --- /dev/null +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -0,0 +1,5321 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@nestjs/common': + specifier: ^10.0.0 + version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': + specifier: ^10.0.0 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-express': + specifier: ^10.0.0 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.13 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + +devDependencies: + '@nestjs/cli': + specifier: ^10.0.0 + version: 10.2.1 + '@nestjs/schematics': + specifier: ^10.0.0 + version: 10.0.3(chokidar@3.5.3)(typescript@5.2.2) + '@nestjs/testing': + specifier: ^10.0.0 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8) + '@types/express': + specifier: ^4.17.17 + version: 4.17.21 + '@types/jest': + specifier: ^29.5.2 + version: 29.5.8 + '@types/node': + specifier: ^20.3.1 + version: 20.9.0 + '@types/supertest': + specifier: ^2.0.12 + version: 2.0.16 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.10.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + eslint: + specifier: ^8.42.0 + version: 8.53.0 + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.0.0(eslint@8.53.0) + eslint-plugin-nestjs: + specifier: ^1.2.3 + version: 1.2.3 + eslint-plugin-prettier: + specifier: ^5.0.0 + version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.0.3) + jest: + specifier: ^29.5.0 + version: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + prettier: + specifier: ^3.0.0 + version: 3.0.3 + source-map-support: + specifier: ^0.5.21 + version: 0.5.21 + supertest: + specifier: ^6.3.3 + version: 6.3.3 + ts-jest: + specifier: ^29.1.0 + version: 29.1.1(@babel/core@7.23.3)(jest@29.7.0)(typescript@5.2.2) + ts-loader: + specifier: ^9.4.3 + version: 9.5.0(typescript@5.2.2)(webpack@5.89.0) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.9.0)(typescript@5.2.2) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + typescript: + specifier: ^5.1.3 + version: 5.2.2 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@angular-devkit/core@16.2.8(chokidar@3.5.3): + resolution: {integrity: sha512-PTGozYvh1Bin5lB15PwcXa26Ayd17bWGLS3H8Rs0s+04mUDvfNofmweaX1LgumWWy3nCUTDuwHxX10M3G0wE2g==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + chokidar: 3.5.3 + jsonc-parser: 3.2.0 + picomatch: 2.3.1 + rxjs: 7.8.1 + source-map: 0.7.4 + dev: true + + /@angular-devkit/schematics-cli@16.2.8(chokidar@3.5.3): + resolution: {integrity: sha512-EXURJCzWTVYCipiTT4vxQQOrF63asOUDbeOy3OtiSh7EwIUvxm3BPG6hquJqngEnI/N6bA75NJ1fBhU6Hrh7eA==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics': 16.2.8(chokidar@3.5.3) + ansi-colors: 4.1.3 + inquirer: 8.2.4 + symbol-observable: 4.0.0 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics@16.2.8(chokidar@3.5.3): + resolution: {integrity: sha512-MBiKZOlR9/YMdflALr7/7w/BGAfo/BGTrlkqsIB6rDWV1dYiCgxI+033HsiNssLS6RQyCFx/e7JA2aBBzu9zEg==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + jsonc-parser: 3.2.0 + magic-string: 0.30.1 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.3: + resolution: {integrity: sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.3: + resolution: {integrity: sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.3 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/helpers': 7.23.2 + '@babel/parser': 7.23.3 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.3 + '@babel/types': 7.23.3 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.3: + resolution: {integrity: sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.3 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.2: + resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.3 + '@babel/types': 7.23.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.3: + resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.3): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.3): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.3): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + dev: true + + /@babel/traverse@7.23.3: + resolution: {integrity: sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.3: + resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.53.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.3: + resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.53.0: + resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0(ts-node@10.9.1): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + jest-mock: 29.7.0 + dev: true + + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.9.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 + '@types/node': 20.9.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.3 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.9.0 + '@types/yargs': 17.0.31 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map@0.3.5: + resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@lukeed/csprng@1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + /@nestjs/cli@10.2.1: + resolution: {integrity: sha512-CAJAQwmxFZfB3RTvqz/eaXXWpyU+mZ4QSqfBYzjneTsPgF+uyOAW3yQpaLNn9Dfcv39R9UxSuAhayv6yuFd+Jg==} + engines: {node: '>= 16.14'} + hasBin: true + peerDependencies: + '@swc/cli': ^0.1.62 + '@swc/core': ^1.3.62 + peerDependenciesMeta: + '@swc/cli': + optional: true + '@swc/core': + optional: true + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics-cli': 16.2.8(chokidar@3.5.3) + '@nestjs/schematics': 10.0.3(chokidar@3.5.3)(typescript@5.2.2) + chalk: 4.1.2 + chokidar: 3.5.3 + cli-table3: 0.6.3 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.2.2)(webpack@5.89.0) + glob: 10.3.10 + inquirer: 8.2.6 + node-emoji: 1.11.0 + ora: 5.4.1 + os-name: 4.0.1 + rimraf: 4.4.1 + shelljs: 0.8.5 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.1.0 + typescript: 5.2.2 + webpack: 5.89.0 + webpack-node-externals: 3.0.0 + transitivePeerDependencies: + - esbuild + - uglify-js + - webpack-cli + dev: true + + /@nestjs/common@10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-rmpwcdvq2IWMmsUVP8rsdKub6uDWk7dwCYo0aif50JTwcvcxzaP3iKVFKoSgvp0RKYu8h15+/AEOfaInmPpl0Q==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + + /@nestjs/core@10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-9+MZ2s8ixfY9Bl/M9ofChiyYymcwdK9ZWNH4GDMF7Am7XRAQ1oqde6MYGG05rhQwiVXuTwaYLlXciJKfsrg5qg==} + requiresBuild: true + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + + /@nestjs/platform-express@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): + resolution: {integrity: sha512-WoSSVtwIRc5AdGMHWVzWZK4JZLT0f4o2xW8P9gQvcX+omL8W1kXCfY8GQYXNBG84XmBNYH8r0FtC8oMe/lH5NQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + body-parser: 1.20.2 + cors: 2.8.5 + express: 4.18.2 + multer: 1.4.4-lts.1 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + + /@nestjs/schematics@10.0.3(chokidar@3.5.3)(typescript@5.2.2): + resolution: {integrity: sha512-2BRujK0GqGQ7j1Zpz+obVfskDnnOeVKt5aXoSaVngKo8Oczy8uYCY+R547TQB+Kf35epdfFER2pVnQrX3/It5A==} + peerDependencies: + typescript: '>=4.8.2' + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics': 16.2.8(chokidar@3.5.3) + comment-json: 4.2.3 + jsonc-parser: 3.2.0 + pluralize: 8.0.0 + typescript: 5.2.2 + transitivePeerDependencies: + - chokidar + dev: true + + /@nestjs/testing@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8): + resolution: {integrity: sha512-9Kj5IQhM67/nj/MT6Wi2OmWr5YQnCMptwKVFrX1TDaikpY12196v7frk0jVjdT7wms7rV07GZle9I2z0aSjqtQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + tslib: 2.6.2 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@nuxtjs/opencollective@0.3.2: + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + + /@pkgr/utils@2.4.2: + resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + fast-glob: 3.3.2 + is-glob: 4.0.3 + open: 9.1.0 + picocolors: 1.0.0 + tslib: 2.6.2 + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/babel__core@7.20.4: + resolution: {integrity: sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==} + dependencies: + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + '@types/babel__generator': 7.6.7 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.4 + dev: true + + /@types/babel__generator@7.6.7: + resolution: {integrity: sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + dev: true + + /@types/babel__traverse@7.20.4: + resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.9.0 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.9.0 + dev: true + + /@types/cookiejar@2.1.4: + resolution: {integrity: sha512-b698BLJ6kPVd6uhHsY7wlebZdrWPXYied883PDSzpJZYOP97EOn/oGdLCH3jJf157srkFReIZY5v0H1s8Dozrg==} + dev: true + + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 8.44.7 + '@types/estree': 1.0.5 + dev: true + + /@types/eslint@8.44.7: + resolution: {integrity: sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==} + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + + /@types/express-serve-static-core@4.17.41: + resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} + dependencies: + '@types/node': 20.9.0 + '@types/qs': 6.9.10 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.41 + '@types/qs': 6.9.10 + '@types/serve-static': 1.15.5 + dev: true + + /@types/graceful-fs@4.1.9: + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + dependencies: + '@types/node': 20.9.0 + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/istanbul-lib-coverage@2.0.6: + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + dev: true + + /@types/istanbul-lib-report@3.0.3: + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + dev: true + + /@types/istanbul-reports@3.0.4: + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + dependencies: + '@types/istanbul-lib-report': 3.0.3 + dev: true + + /@types/jest@29.5.8: + resolution: {integrity: sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + + /@types/node@20.9.0: + resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/qs@6.9.10: + resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/semver@7.5.5: + resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.9.0 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.9.0 + dev: true + + /@types/stack-utils@2.0.3: + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + dev: true + + /@types/superagent@4.1.21: + resolution: {integrity: sha512-yrbAccEEY9+BSa1wji3ry8R3/BdW9kyWnjkRKctrtw5ebn/k2a2CsMeaQ7dD4iLfomgHkomBVIVgOFRMV4XYHA==} + dependencies: + '@types/cookiejar': 2.1.4 + '@types/node': 20.9.0 + dev: true + + /@types/supertest@2.0.16: + resolution: {integrity: sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==} + dependencies: + '@types/superagent': 4.1.21 + dev: true + + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.31: + resolution: {integrity: sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==} + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + + /@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.10.0 + '@typescript-eslint/type-utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.10.0 + debug: 4.3.4 + eslint: 8.53.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.10.0 + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.10.0 + debug: 4.3.4 + eslint: 8.53.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.10.0: + resolution: {integrity: sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/visitor-keys': 6.10.0 + dev: true + + /@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.53.0 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.10.0: + resolution: {integrity: sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2): + resolution: {integrity: sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/visitor-keys': 6.10.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.5 + '@typescript-eslint/scope-manager': 6.10.0 + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + eslint: 8.53.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.10.0: + resolution: {integrity: sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.10.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@webassemblyjs/ast@1.11.6: + resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + dev: true + + /@webassemblyjs/floating-point-hex-parser@1.11.6: + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + dev: true + + /@webassemblyjs/helper-api-error@1.11.6: + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + dev: true + + /@webassemblyjs/helper-buffer@1.11.6: + resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==} + dev: true + + /@webassemblyjs/helper-numbers@1.11.6: + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/helper-wasm-bytecode@1.11.6: + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + dev: true + + /@webassemblyjs/helper-wasm-section@1.11.6: + resolution: {integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + dev: true + + /@webassemblyjs/ieee754@1.11.6: + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + dependencies: + '@xtuc/ieee754': 1.2.0 + dev: true + + /@webassemblyjs/leb128@1.11.6: + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + dependencies: + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/utf8@1.11.6: + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + dev: true + + /@webassemblyjs/wasm-edit@1.11.6: + resolution: {integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-opt': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + '@webassemblyjs/wast-printer': 1.11.6 + dev: true + + /@webassemblyjs/wasm-gen@1.11.6: + resolution: {integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wasm-opt@1.11.6: + resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + dev: true + + /@webassemblyjs/wasm-parser@1.11.6: + resolution: {integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wast-printer@1.11.6: + resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@xtuc/ieee754@1.2.0: + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + dev: true + + /@xtuc/long@4.2.2: + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + dev: true + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + /acorn-import-assertions@1.9.0(acorn@8.11.2): + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn-walk@8.3.0: + resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv-formats@2.1.1(ajv@8.12.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + dev: true + + /ajv-keywords@3.5.2(ajv@6.12.6): + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + dependencies: + ajv: 6.12.6 + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + /array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + + /babel-jest@29.7.0(@babel/core@7.23.3): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.23.3 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.4 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.23.3) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.22.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.3 + '@types/babel__core': 7.20.4 + '@types/babel__traverse': 7.20.4 + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.3): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.3) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.3) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.3): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.3) + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /body-parser@1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + /bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + dependencies: + big-integer: 1.6.51 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001561 + electron-to-chromium: 1.4.581 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + dependencies: + run-applescript: 5.0.0 + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001561: + resolution: {integrity: sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /chrome-trace-event@1.0.3: + resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + engines: {node: '>=6.0'} + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + + /cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: true + + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners@2.9.1: + resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} + engines: {node: '>=6'} + dev: true + + /cli-table3@0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /comment-json@4.2.3: + resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} + engines: {node: '>= 6'} + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + has-own-prop: 2.0.0 + repeat-string: 1.6.1 + dev: true + + /component-emitter@1.3.0: + resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + /consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + + /cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + dev: true + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + /cosmiconfig@8.3.6(typescript@5.2.2): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.2.2 + dev: true + + /create-jest@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + dev: true + + /default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.2.0 + titleize: 3.0.0 + dev: true + + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 + dev: true + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + + /define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + /electron-to-chromium@1.4.581: + resolution: {integrity: sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==} + dev: true + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + + /enhanced-resolve@5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-module-lexer@1.4.0: + resolution: {integrity: sha512-lcCr3v3OLezdfFyx9r5NRYHOUTQNnFEQ9E87Mx8Kc+iqyJNkO7MJoB4GQRTlIMw9kLLTwGw0OAkm4BQQud/d9g==} + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier@9.0.0(eslint@8.53.0): + resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.53.0 + dev: true + + /eslint-plugin-nestjs@1.2.3: + resolution: {integrity: sha512-CYS2l+oO9sZ8QN1B0/Xgz+2CERfiWCiHDmDslX30yrJrNlBNKFypeCac/7g/NE+LDuox5MH13uvd4qd52Tlt5w==} + engines: {npm: '>=3'} + dependencies: + tslib: 1.14.1 + dev: true + + /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.0.3): + resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.53.0 + eslint-config-prettier: 9.0.0(eslint@8.53.0) + prettier: 3.0.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.5 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.53.0: + resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.3 + '@eslint/js': 8.53.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) + eslint-visitor-keys: 3.4.3 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + + /execa@4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + + /express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + + /fork-ts-checker-webpack-plugin@9.0.2(typescript@5.2.2)(webpack@5.89.0): + resolution: {integrity: sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + dependencies: + '@babel/code-frame': 7.22.13 + chalk: 4.1.2 + chokidar: 3.5.3 + cosmiconfig: 8.3.6(typescript@5.2.2) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.5.4 + tapable: 2.2.1 + typescript: 5.2.2 + webpack: 5.89.0 + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /formidable@2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + dependencies: + dezalgo: 1.0.4 + hexoid: 1.0.0 + once: 1.4.0 + qs: 6.11.2 + dev: true + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + /fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-monkey@1.0.5: + resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.10.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + /has-own-prop@2.0.0: + resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + + /hexoid@1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + /human-signals@1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /inquirer@8.2.4: + resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + dev: true + + /interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + dev: true + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + is-docker: 3.0.0 + dev: true + + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.23.3 + '@babel/parser': 7.23.3 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.1: + resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.23.3 + '@babel/parser': 7.23.3 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.0.4 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.23.3 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + babel-jest: 29.7.0(@babel/core@7.23.3) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@20.9.0)(typescript@5.2.2) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 20.9.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.22.13 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + jest-util: 29.7.0 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.3 + '@babel/generator': 7.23.3 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) + '@babel/types': 7.23.3 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.3) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + + /jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 20.9.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 20.9.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /lru-cache@10.0.2: + resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==} + engines: {node: 14 || >=16.14} + dependencies: + semver: 7.5.4 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /macos-release@2.5.1: + resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} + engines: {node: '>=6'} + dev: true + + /magic-string@0.30.1: + resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + /memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + dependencies: + fs-monkey: 1.0.5 + dev: true + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + /mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + /multer@1.4.4-lts.1: + resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} + engines: {node: '>= 6.0.0'} + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + /neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + dev: true + + /node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + dev: true + + /node-emoji@1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + dependencies: + lodash: 4.17.21 + dev: true + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.1 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + + /os-name@4.0.1: + resolution: {integrity: sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==} + engines: {node: '>=10'} + dependencies: + macos-release: 2.5.1 + windows-release: 4.0.0 + dev: true + + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.0.2 + minipass: 7.0.4 + dev: true + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + + /path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + + /qs@6.11.2: + resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + /raw-body@2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + dependencies: + resolve: 1.22.8 + dev: true + + /reflect-metadata@0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + + /repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@4.4.1: + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 9.3.5 + dev: true + + /run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + dev: true + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.6.2 + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + /schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + /serialize-javascript@6.0.1: + resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} + dependencies: + randombytes: 2.1.0 + dev: true + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /superagent@8.1.2: + resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} + engines: {node: '>=6.4.0 <13 || >=14'} + dependencies: + component-emitter: 1.3.0 + cookiejar: 2.1.4 + debug: 4.3.4 + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 2.1.2 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.11.2 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /supertest@6.3.3: + resolution: {integrity: sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==} + engines: {node: '>=6.4.0'} + dependencies: + methods: 1.1.2 + superagent: 8.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + dev: true + + /synckit@0.8.5: + resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/utils': 2.4.2 + tslib: 2.6.2 + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + + /terser-webpack-plugin@5.3.9(webpack@5.89.0): + resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.1 + terser: 5.24.0 + webpack: 5.89.0 + dev: true + + /terser@5.24.0: + resolution: {integrity: sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.11.2 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + dev: true + + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /ts-jest@29.1.1(@babel/core@7.23.3)(jest@29.7.0)(typescript@5.2.2): + resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.23.3 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.4 + typescript: 5.2.2 + yargs-parser: 21.1.1 + dev: true + + /ts-loader@9.5.0(typescript@5.2.2)(webpack@5.89.0): + resolution: {integrity: sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.15.0 + micromatch: 4.0.5 + semver: 7.5.4 + source-map: 0.7.4 + typescript: 5.2.2 + webpack: 5.89.0 + dev: true + + /ts-node@10.9.1(@types/node@20.9.0)(typescript@5.2.2): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.9.0 + acorn: 8.11.2 + acorn-walk: 8.3.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.2.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths-webpack-plugin@4.1.0: + resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} + engines: {node: '>=10.13.0'} + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.15.0 + tsconfig-paths: 4.2.0 + dev: true + + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + /typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: true + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + dev: true + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + dev: true + + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + /webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + dev: true + + /webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + dev: true + + /webpack@5.89.0: + resolution: {integrity: sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/wasm-edit': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + acorn: 8.11.2 + acorn-import-assertions: 1.9.0(acorn@8.11.2) + browserslist: 4.22.1 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.4.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.9(webpack@5.89.0) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /windows-release@4.0.0: + resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} + engines: {node: '>=10'} + dependencies: + execa: 4.1.0 + dev: true + + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/be/algo-with-me-api/src/app.controller.spec.ts b/be/algo-with-me-api/src/app.controller.spec.ts new file mode 100644 index 0000000..ccea57f --- /dev/null +++ b/be/algo-with-me-api/src/app.controller.spec.ts @@ -0,0 +1,23 @@ +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.getA progressive Node.js framework for building efficient and scalable server-side applications.
+ + + +## Description + +알고리즘 대회 서비스 채점 서버 + +## Installation + +```bash +$ pnpm install +``` + +## Running the app + +```bash +# development +$ pnpm run start + +# watch mode +$ pnpm run start:dev + +# production mode +$ pnpm run start:prod +``` + +## Test + +```bash +# unit tests +$ pnpm run test + +# e2e tests +$ pnpm run test:e2e + +# test coverage +$ pnpm run test:cov +``` diff --git a/be/algo-with-me-score/nest-cli.json b/be/algo-with-me-score/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/be/algo-with-me-score/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json new file mode 100644 index 0000000..52aa189 --- /dev/null +++ b/be/algo-with-me-score/package.json @@ -0,0 +1,70 @@ +{ + "name": "algo-with-me-api", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@nestjs/cli": "^10.0.0", + "@nestjs/schematics": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/express": "^4.17.17", + "@types/jest": "^29.5.2", + "@types/node": "^20.3.1", + "@types/supertest": "^2.0.12", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-nestjs": "^1.2.3", + "eslint-plugin-prettier": "^5.0.0", + "jest": "^29.5.0", + "prettier": "^3.0.0", + "source-map-support": "^0.5.21", + "supertest": "^6.3.3", + "ts-jest": "^29.1.0", + "ts-loader": "^9.4.3", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.1.3" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml new file mode 100644 index 0000000..103b829 --- /dev/null +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -0,0 +1,5321 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@nestjs/common': + specifier: ^10.0.0 + version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': + specifier: ^10.0.0 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-express': + specifier: ^10.0.0 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.13 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + +devDependencies: + '@nestjs/cli': + specifier: ^10.0.0 + version: 10.2.1 + '@nestjs/schematics': + specifier: ^10.0.0 + version: 10.0.3(chokidar@3.5.3)(typescript@5.2.2) + '@nestjs/testing': + specifier: ^10.0.0 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8) + '@types/express': + specifier: ^4.17.17 + version: 4.17.21 + '@types/jest': + specifier: ^29.5.2 + version: 29.5.8 + '@types/node': + specifier: ^20.3.1 + version: 20.9.0 + '@types/supertest': + specifier: ^2.0.12 + version: 2.0.16 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.10.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + eslint: + specifier: ^8.42.0 + version: 8.53.0 + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.0.0(eslint@8.53.0) + eslint-plugin-nestjs: + specifier: ^1.2.3 + version: 1.2.3 + eslint-plugin-prettier: + specifier: ^5.0.0 + version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.0.3) + jest: + specifier: ^29.5.0 + version: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + prettier: + specifier: ^3.0.0 + version: 3.0.3 + source-map-support: + specifier: ^0.5.21 + version: 0.5.21 + supertest: + specifier: ^6.3.3 + version: 6.3.3 + ts-jest: + specifier: ^29.1.0 + version: 29.1.1(@babel/core@7.23.3)(jest@29.7.0)(typescript@5.2.2) + ts-loader: + specifier: ^9.4.3 + version: 9.5.0(typescript@5.2.2)(webpack@5.89.0) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.9.0)(typescript@5.2.2) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + typescript: + specifier: ^5.1.3 + version: 5.2.2 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@angular-devkit/core@16.2.8(chokidar@3.5.3): + resolution: {integrity: sha512-PTGozYvh1Bin5lB15PwcXa26Ayd17bWGLS3H8Rs0s+04mUDvfNofmweaX1LgumWWy3nCUTDuwHxX10M3G0wE2g==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + chokidar: 3.5.3 + jsonc-parser: 3.2.0 + picomatch: 2.3.1 + rxjs: 7.8.1 + source-map: 0.7.4 + dev: true + + /@angular-devkit/schematics-cli@16.2.8(chokidar@3.5.3): + resolution: {integrity: sha512-EXURJCzWTVYCipiTT4vxQQOrF63asOUDbeOy3OtiSh7EwIUvxm3BPG6hquJqngEnI/N6bA75NJ1fBhU6Hrh7eA==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics': 16.2.8(chokidar@3.5.3) + ansi-colors: 4.1.3 + inquirer: 8.2.4 + symbol-observable: 4.0.0 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics@16.2.8(chokidar@3.5.3): + resolution: {integrity: sha512-MBiKZOlR9/YMdflALr7/7w/BGAfo/BGTrlkqsIB6rDWV1dYiCgxI+033HsiNssLS6RQyCFx/e7JA2aBBzu9zEg==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + jsonc-parser: 3.2.0 + magic-string: 0.30.1 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.3: + resolution: {integrity: sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.3: + resolution: {integrity: sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.3 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/helpers': 7.23.2 + '@babel/parser': 7.23.3 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.3 + '@babel/types': 7.23.3 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.3: + resolution: {integrity: sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.3 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.2: + resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.3 + '@babel/types': 7.23.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.3: + resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.3): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.3): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.3): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + dev: true + + /@babel/traverse@7.23.3: + resolution: {integrity: sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.3: + resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.53.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.3: + resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.53.0: + resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0(ts-node@10.9.1): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + jest-mock: 29.7.0 + dev: true + + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.9.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 + '@types/node': 20.9.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.3 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.9.0 + '@types/yargs': 17.0.31 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map@0.3.5: + resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@lukeed/csprng@1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + /@nestjs/cli@10.2.1: + resolution: {integrity: sha512-CAJAQwmxFZfB3RTvqz/eaXXWpyU+mZ4QSqfBYzjneTsPgF+uyOAW3yQpaLNn9Dfcv39R9UxSuAhayv6yuFd+Jg==} + engines: {node: '>= 16.14'} + hasBin: true + peerDependencies: + '@swc/cli': ^0.1.62 + '@swc/core': ^1.3.62 + peerDependenciesMeta: + '@swc/cli': + optional: true + '@swc/core': + optional: true + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics-cli': 16.2.8(chokidar@3.5.3) + '@nestjs/schematics': 10.0.3(chokidar@3.5.3)(typescript@5.2.2) + chalk: 4.1.2 + chokidar: 3.5.3 + cli-table3: 0.6.3 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.2.2)(webpack@5.89.0) + glob: 10.3.10 + inquirer: 8.2.6 + node-emoji: 1.11.0 + ora: 5.4.1 + os-name: 4.0.1 + rimraf: 4.4.1 + shelljs: 0.8.5 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.1.0 + typescript: 5.2.2 + webpack: 5.89.0 + webpack-node-externals: 3.0.0 + transitivePeerDependencies: + - esbuild + - uglify-js + - webpack-cli + dev: true + + /@nestjs/common@10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-rmpwcdvq2IWMmsUVP8rsdKub6uDWk7dwCYo0aif50JTwcvcxzaP3iKVFKoSgvp0RKYu8h15+/AEOfaInmPpl0Q==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + + /@nestjs/core@10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-9+MZ2s8ixfY9Bl/M9ofChiyYymcwdK9ZWNH4GDMF7Am7XRAQ1oqde6MYGG05rhQwiVXuTwaYLlXciJKfsrg5qg==} + requiresBuild: true + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + + /@nestjs/platform-express@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): + resolution: {integrity: sha512-WoSSVtwIRc5AdGMHWVzWZK4JZLT0f4o2xW8P9gQvcX+omL8W1kXCfY8GQYXNBG84XmBNYH8r0FtC8oMe/lH5NQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + body-parser: 1.20.2 + cors: 2.8.5 + express: 4.18.2 + multer: 1.4.4-lts.1 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + + /@nestjs/schematics@10.0.3(chokidar@3.5.3)(typescript@5.2.2): + resolution: {integrity: sha512-2BRujK0GqGQ7j1Zpz+obVfskDnnOeVKt5aXoSaVngKo8Oczy8uYCY+R547TQB+Kf35epdfFER2pVnQrX3/It5A==} + peerDependencies: + typescript: '>=4.8.2' + dependencies: + '@angular-devkit/core': 16.2.8(chokidar@3.5.3) + '@angular-devkit/schematics': 16.2.8(chokidar@3.5.3) + comment-json: 4.2.3 + jsonc-parser: 3.2.0 + pluralize: 8.0.0 + typescript: 5.2.2 + transitivePeerDependencies: + - chokidar + dev: true + + /@nestjs/testing@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8): + resolution: {integrity: sha512-9Kj5IQhM67/nj/MT6Wi2OmWr5YQnCMptwKVFrX1TDaikpY12196v7frk0jVjdT7wms7rV07GZle9I2z0aSjqtQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + tslib: 2.6.2 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@nuxtjs/opencollective@0.3.2: + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + + /@pkgr/utils@2.4.2: + resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + fast-glob: 3.3.2 + is-glob: 4.0.3 + open: 9.1.0 + picocolors: 1.0.0 + tslib: 2.6.2 + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/babel__core@7.20.4: + resolution: {integrity: sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==} + dependencies: + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + '@types/babel__generator': 7.6.7 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.4 + dev: true + + /@types/babel__generator@7.6.7: + resolution: {integrity: sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.23.3 + '@babel/types': 7.23.3 + dev: true + + /@types/babel__traverse@7.20.4: + resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.9.0 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.9.0 + dev: true + + /@types/cookiejar@2.1.4: + resolution: {integrity: sha512-b698BLJ6kPVd6uhHsY7wlebZdrWPXYied883PDSzpJZYOP97EOn/oGdLCH3jJf157srkFReIZY5v0H1s8Dozrg==} + dev: true + + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 8.44.7 + '@types/estree': 1.0.5 + dev: true + + /@types/eslint@8.44.7: + resolution: {integrity: sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==} + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + + /@types/express-serve-static-core@4.17.41: + resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} + dependencies: + '@types/node': 20.9.0 + '@types/qs': 6.9.10 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.41 + '@types/qs': 6.9.10 + '@types/serve-static': 1.15.5 + dev: true + + /@types/graceful-fs@4.1.9: + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + dependencies: + '@types/node': 20.9.0 + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/istanbul-lib-coverage@2.0.6: + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + dev: true + + /@types/istanbul-lib-report@3.0.3: + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + dev: true + + /@types/istanbul-reports@3.0.4: + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + dependencies: + '@types/istanbul-lib-report': 3.0.3 + dev: true + + /@types/jest@29.5.8: + resolution: {integrity: sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + + /@types/node@20.9.0: + resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/qs@6.9.10: + resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/semver@7.5.5: + resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.9.0 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.9.0 + dev: true + + /@types/stack-utils@2.0.3: + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + dev: true + + /@types/superagent@4.1.21: + resolution: {integrity: sha512-yrbAccEEY9+BSa1wji3ry8R3/BdW9kyWnjkRKctrtw5ebn/k2a2CsMeaQ7dD4iLfomgHkomBVIVgOFRMV4XYHA==} + dependencies: + '@types/cookiejar': 2.1.4 + '@types/node': 20.9.0 + dev: true + + /@types/supertest@2.0.16: + resolution: {integrity: sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==} + dependencies: + '@types/superagent': 4.1.21 + dev: true + + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.31: + resolution: {integrity: sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==} + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + + /@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.10.0 + '@typescript-eslint/type-utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.10.0 + debug: 4.3.4 + eslint: 8.53.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.10.0 + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.10.0 + debug: 4.3.4 + eslint: 8.53.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.10.0: + resolution: {integrity: sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/visitor-keys': 6.10.0 + dev: true + + /@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.53.0 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.10.0: + resolution: {integrity: sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2): + resolution: {integrity: sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/visitor-keys': 6.10.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.5 + '@typescript-eslint/scope-manager': 6.10.0 + '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + eslint: 8.53.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.10.0: + resolution: {integrity: sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.10.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@webassemblyjs/ast@1.11.6: + resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + dev: true + + /@webassemblyjs/floating-point-hex-parser@1.11.6: + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + dev: true + + /@webassemblyjs/helper-api-error@1.11.6: + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + dev: true + + /@webassemblyjs/helper-buffer@1.11.6: + resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==} + dev: true + + /@webassemblyjs/helper-numbers@1.11.6: + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/helper-wasm-bytecode@1.11.6: + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + dev: true + + /@webassemblyjs/helper-wasm-section@1.11.6: + resolution: {integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + dev: true + + /@webassemblyjs/ieee754@1.11.6: + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + dependencies: + '@xtuc/ieee754': 1.2.0 + dev: true + + /@webassemblyjs/leb128@1.11.6: + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + dependencies: + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/utf8@1.11.6: + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + dev: true + + /@webassemblyjs/wasm-edit@1.11.6: + resolution: {integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-opt': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + '@webassemblyjs/wast-printer': 1.11.6 + dev: true + + /@webassemblyjs/wasm-gen@1.11.6: + resolution: {integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wasm-opt@1.11.6: + resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + dev: true + + /@webassemblyjs/wasm-parser@1.11.6: + resolution: {integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wast-printer@1.11.6: + resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@xtuc/ieee754@1.2.0: + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + dev: true + + /@xtuc/long@4.2.2: + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + dev: true + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + /acorn-import-assertions@1.9.0(acorn@8.11.2): + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn-walk@8.3.0: + resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv-formats@2.1.1(ajv@8.12.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + dev: true + + /ajv-keywords@3.5.2(ajv@6.12.6): + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + dependencies: + ajv: 6.12.6 + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + /array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + + /babel-jest@29.7.0(@babel/core@7.23.3): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.23.3 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.4 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.23.3) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.22.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.3 + '@types/babel__core': 7.20.4 + '@types/babel__traverse': 7.20.4 + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.3): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.3) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.3) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.3): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.3) + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /body-parser@1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + /bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + dependencies: + big-integer: 1.6.51 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001561 + electron-to-chromium: 1.4.581 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + dependencies: + run-applescript: 5.0.0 + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001561: + resolution: {integrity: sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /chrome-trace-event@1.0.3: + resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + engines: {node: '>=6.0'} + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + + /cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: true + + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners@2.9.1: + resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} + engines: {node: '>=6'} + dev: true + + /cli-table3@0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /comment-json@4.2.3: + resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} + engines: {node: '>= 6'} + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + has-own-prop: 2.0.0 + repeat-string: 1.6.1 + dev: true + + /component-emitter@1.3.0: + resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + /consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + + /cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + dev: true + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + /cosmiconfig@8.3.6(typescript@5.2.2): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.2.2 + dev: true + + /create-jest@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + dev: true + + /default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.2.0 + titleize: 3.0.0 + dev: true + + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 + dev: true + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + + /define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + /electron-to-chromium@1.4.581: + resolution: {integrity: sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==} + dev: true + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + + /enhanced-resolve@5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-module-lexer@1.4.0: + resolution: {integrity: sha512-lcCr3v3OLezdfFyx9r5NRYHOUTQNnFEQ9E87Mx8Kc+iqyJNkO7MJoB4GQRTlIMw9kLLTwGw0OAkm4BQQud/d9g==} + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier@9.0.0(eslint@8.53.0): + resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.53.0 + dev: true + + /eslint-plugin-nestjs@1.2.3: + resolution: {integrity: sha512-CYS2l+oO9sZ8QN1B0/Xgz+2CERfiWCiHDmDslX30yrJrNlBNKFypeCac/7g/NE+LDuox5MH13uvd4qd52Tlt5w==} + engines: {npm: '>=3'} + dependencies: + tslib: 1.14.1 + dev: true + + /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.0.3): + resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.53.0 + eslint-config-prettier: 9.0.0(eslint@8.53.0) + prettier: 3.0.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.5 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.53.0: + resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.3 + '@eslint/js': 8.53.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) + eslint-visitor-keys: 3.4.3 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + + /execa@4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + + /express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + + /fork-ts-checker-webpack-plugin@9.0.2(typescript@5.2.2)(webpack@5.89.0): + resolution: {integrity: sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + dependencies: + '@babel/code-frame': 7.22.13 + chalk: 4.1.2 + chokidar: 3.5.3 + cosmiconfig: 8.3.6(typescript@5.2.2) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.5.4 + tapable: 2.2.1 + typescript: 5.2.2 + webpack: 5.89.0 + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /formidable@2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + dependencies: + dezalgo: 1.0.4 + hexoid: 1.0.0 + once: 1.4.0 + qs: 6.11.2 + dev: true + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + /fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-monkey@1.0.5: + resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.10.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + /has-own-prop@2.0.0: + resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + + /hexoid@1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + /human-signals@1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /inquirer@8.2.4: + resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + dev: true + + /interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + dev: true + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + is-docker: 3.0.0 + dev: true + + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.23.3 + '@babel/parser': 7.23.3 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.1: + resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.23.3 + '@babel/parser': 7.23.3 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.0.4 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.23.3 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + babel-jest: 29.7.0(@babel/core@7.23.3) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@20.9.0)(typescript@5.2.2) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 20.9.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.22.13 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + jest-util: 29.7.0 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.3 + '@babel/generator': 7.23.3 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) + '@babel/types': 7.23.3 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.3) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.9.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + + /jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 20.9.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 20.9.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.7.0(@types/node@20.9.0)(ts-node@10.9.1): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /lru-cache@10.0.2: + resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==} + engines: {node: 14 || >=16.14} + dependencies: + semver: 7.5.4 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /macos-release@2.5.1: + resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} + engines: {node: '>=6'} + dev: true + + /magic-string@0.30.1: + resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + /memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + dependencies: + fs-monkey: 1.0.5 + dev: true + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + /mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + /multer@1.4.4-lts.1: + resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} + engines: {node: '>= 6.0.0'} + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + /neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + dev: true + + /node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + dev: true + + /node-emoji@1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + dependencies: + lodash: 4.17.21 + dev: true + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.1 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + + /os-name@4.0.1: + resolution: {integrity: sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==} + engines: {node: '>=10'} + dependencies: + macos-release: 2.5.1 + windows-release: 4.0.0 + dev: true + + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.0.2 + minipass: 7.0.4 + dev: true + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + + /path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + + /qs@6.11.2: + resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + /raw-body@2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + dependencies: + resolve: 1.22.8 + dev: true + + /reflect-metadata@0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + + /repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@4.4.1: + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 9.3.5 + dev: true + + /run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + dev: true + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.6.2 + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + /schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + /serialize-javascript@6.0.1: + resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} + dependencies: + randombytes: 2.1.0 + dev: true + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /superagent@8.1.2: + resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} + engines: {node: '>=6.4.0 <13 || >=14'} + dependencies: + component-emitter: 1.3.0 + cookiejar: 2.1.4 + debug: 4.3.4 + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 2.1.2 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.11.2 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /supertest@6.3.3: + resolution: {integrity: sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==} + engines: {node: '>=6.4.0'} + dependencies: + methods: 1.1.2 + superagent: 8.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + dev: true + + /synckit@0.8.5: + resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/utils': 2.4.2 + tslib: 2.6.2 + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + + /terser-webpack-plugin@5.3.9(webpack@5.89.0): + resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.1 + terser: 5.24.0 + webpack: 5.89.0 + dev: true + + /terser@5.24.0: + resolution: {integrity: sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.11.2 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + dev: true + + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /ts-jest@29.1.1(@babel/core@7.23.3)(jest@29.7.0)(typescript@5.2.2): + resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.23.3 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.9.0)(ts-node@10.9.1) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.4 + typescript: 5.2.2 + yargs-parser: 21.1.1 + dev: true + + /ts-loader@9.5.0(typescript@5.2.2)(webpack@5.89.0): + resolution: {integrity: sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.15.0 + micromatch: 4.0.5 + semver: 7.5.4 + source-map: 0.7.4 + typescript: 5.2.2 + webpack: 5.89.0 + dev: true + + /ts-node@10.9.1(@types/node@20.9.0)(typescript@5.2.2): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.9.0 + acorn: 8.11.2 + acorn-walk: 8.3.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.2.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths-webpack-plugin@4.1.0: + resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} + engines: {node: '>=10.13.0'} + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.15.0 + tsconfig-paths: 4.2.0 + dev: true + + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + /typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: true + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + dev: true + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + dev: true + + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + /webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + dev: true + + /webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + dev: true + + /webpack@5.89.0: + resolution: {integrity: sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/wasm-edit': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + acorn: 8.11.2 + acorn-import-assertions: 1.9.0(acorn@8.11.2) + browserslist: 4.22.1 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.4.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.9(webpack@5.89.0) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /windows-release@4.0.0: + resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} + engines: {node: '>=10'} + dependencies: + execa: 4.1.0 + dev: true + + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/be/algo-with-me-score/src/app.controller.spec.ts b/be/algo-with-me-score/src/app.controller.spec.ts new file mode 100644 index 0000000..ccea57f --- /dev/null +++ b/be/algo-with-me-score/src/app.controller.spec.ts @@ -0,0 +1,23 @@ +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.getL&3fdU$vrxMvTr7AM!jq5=tpd)e|(uzSs_ Ti%)C@GN zsadnCHW95|!J9y83O+P^Teo*IjN3TTgd17Wkd8}x#G|P~e3Jhc|2su$+il&!^9lDr z`P b<%Ey{-U#UIF@D z1?qhf^x6E`S-{Sw0`$EF=oA-cv%k0i{Uq!t;91KzK$y*MZ-PFX{p|(hVV+Ul? eNLk@RcvI6yq^3vd|mg<~uhkM_pB;jUn0f7Itq2BVR#h&N1Dh(niMJ*iNL z^=e5ivY$XSmh1{eBfSh4-gqw=j;4~c8A=3DvDF-@1tWge6OCyRvX=~owP=suI|x#| zk0pYrHN^V8!Q@KTgC 4S~a(Jnf8FD(NHhF>kMy zAoulvl3?0^=Gz}*UD`k}$;h7+=hv{U9{0vgT`Ou=)~+(Hr>|B{U#_TK&AQwjtzGb@ z)*DPDwRlJCCip|^@OFpDZ@ppoP#>f&U36AKiuFZ^`Se|c7_US9d)^Gp%rCKUN=%wI zWCh)rOmaU=el$3rcCC=0T^S56q|9MJ;17T9FR-l?vp*Gh>SH8d#4^CC$UH{o4{ViA zzaa7(Ngo&aBNO?Hq=%bz`BDqryHTfCSm>V-^hFl>gRQ!}Vxf- J{N4_fF?2>M|Q{ilNdpoRXBsP~A4{-%(B$U>jLN$>Ab3;hN`FOBJZIZq_6Sm-N+ ze6xkVPSE#R=*@yYWT9^s^dlDfO@cmdq3;#+2@BmP= 2%=pQxF+f4M2ndrMs^s7zueI~lRHz#($M8D1?A2ZRfH_-=7bj3s;GSP1^(GQyF zi%s;yCi=%s^aoA!PnhU)mvbJN^T3=3<~%UxfjJM%d0@^1a~_!Uz?=u>JTT{h|B4=X z%YNNE>hNo&>R8F+I~Y?($CE|*)9Uca(x 7G3!oca^Yby6G1E+(YsoYl$oDO)Va-TDBI@p=Y9WZb@jG4;aVc>Ky zHkI=lI34&*lv$S9sQMk F?2+S3Q{ln Y-W3-q->;Tb|&bOw2pFP7__K_5tf=UO`c2B11b{yZS znWj1}=llg)a;oGv_=c9EK3(!Ny5!jE3C=sA^O{}&%yz;gwd-; UROUXgaY;*m%C;g@;{i>RNG5tpN@1Zw*A#We~JxmX0cMV@q?Dzg9;)~liZ^UDp zxG_J`!`H#75--f!&K|d4KSa$qRnpG!Zwg$^lr*9yRR1Z^&ps#z0R2mUin>XxkARiC z6JcU{fWQwLd^_=PnZZ9*@^kdVmTLi(+K+lJLp`^nPYWk-(AowAi(tT$DR~K~npt=o zR hy3{>mSshN3Csp%F+MfTIb&*!r(&`v)!I&MGch92g^hZuX3_J!lL8F(^&hnkH+ zP^am0DCAi0PM_lXn1*K;l0W$1T?T&rIZyg!6+`+zewWX$dn)zsRCShDo&7aP>evI0 zNucW3A;$!VBb==+9pNpcbwJ|kx_`7ET8NoE*5X*NRv+R@Xm_(GeU7&+`*T?Hq+dTy zjmka;WT
!aT>xz4 zS^Eh6?%+7ugjF|D|3gov<_icCSWTazo}53grZJ{{r~G8M%H4O8UnpmnW0B|q$NuC+ zHT}9e{FE*G)puxuypf#`h)9q^B~-)xFg|qAX&<3q5e{9vHT9hR(M`7X5&K9D2 Nf F_I9#3 zc2}u7{Jm0_4beZJI_vhmn|+z+SimC(&~?vP$*V3Jt?wc(ouRAD!W*GYi Ho>D>&2V+$YKA{R=gay|aYqsO@yAak647L+|o(sKS>0Ypxj%W<9iArFL= z2^fJ}LI`PmeqevdIKO-Y)Ng>)u{vt|*tL(e7*#a|Db9{9$a=~A*n(r29Adp@nno!y zs)B)NZZMu8#&W?}Z(_VfjH?9WxQX#Gg!$~d=k+>1Z(^Jv#;*k9GbTp!wP1W-FxHwF zUn9o11*6%-I7N&v3I_cGLytR|rr#1{NHE}9gK;JPSeNY;49Y{L#$8BKv)gCUC?k#4 zg5fY}e3Tg12u6d6QALbm!KgPeb`ay$bGl0?iItUKMU_7<7)wlyPSSWxFt8>J4L>oy zGK;BKkbGzFnZ?vCq!AVj41r b4uX3pA6Hqt}1$_+pR(CxqP&}ti`Y7-Il!?uV zeYIde2KL!s3d;}E1~~+ZVf^e@$;LQHXO9`XX9cDP-|vW*1<##EXDdHDvlU-_8`V6F zW|P!uNaa!#S=&M8jlOxN`0XS YQ9$az#y|Q)`^ZBa!}g4LN-a32yE;P? zfxN7b&BJe@)&I=H&LPJ`WRbFa6&qY2=fjomSo=LUb1KG54&6D}gd2pvVIH^*IQmO< z*-qpfT;-7C2w80u$YDZi1VXNVl7gU`eul>5k~@9co&K(R;G!K#s{Oui+c@iZ1+Z3B z1`*npsowNyu7FW{5O>kMFYpbvhJTwvhb|##$d>;#kMHl-a%yZpq|o+eOyHa%n)eK~ z^#G}3^FT7jvOj$j#=lAtNp2@6iSiOC9wc5BAiKvlWbX&@>>`p;Nb4|Akb!4YbUFNV zse5<>OT9=b##@t=px8&Y!AAB`An4K~73N+0TU5m|Vt+wr|HTaUpAowYY~)q}`=j5> zQMk^AbkUV0dKVhO)16)t{W9ihww2gp3-3cp#o4IzdWdo<7fCfj3dGgel}sz|9%A)l z`B8AF41h@+a@G#=V;C!zn}@n*EHkOhmzyX3Jk-wPSj3$^fw5Xk=GrODWh#FMsuTw6 z8e*;G5wOLf(ELq0N^|Ql7`SaaX5Q3_une>RivDB+Z&2kWH#N~RSoq&=lm;`E _xS0Ozkvjm?EN6}@xv&2(wDMbWbKb(%{^wXr^a2t1RPsbKc0Ru`*HB<^TWR@ zYPLV{^zhSvl3fhB>|T6LC!gZHtNu6c^z*l=xE&e()nr=-ZaM3pMK*N@EaWTqVBWty ztpa+E+MOwRnZUXW$x3wPMhMaE?EL(M$e)lf4pEyS|2UUl{cQr(mngYE@%je!#D!wj zc1nHzQu0a|s1XKA<^8MFr}MX=rWABpYH6oB-1JBKI(#MhAtbt=0Fu4<8+6Q%ww@|! zg`90KbeUN{*AJ#>Pp6uGmyQ)0FbzD|5stvHC#{E+k?~}+y?TU4ay#y%#=)~!Kfq(E zz51^l6CD2=erIE^{#H=w+{d*ipgZ6AlH?bWTBg2a>Zxjv?sP=iyd0Gva82(9`oA z3TVgZON9WZwK(S00k8iTHIsBG{6@%=_BbleE~ER#^l2BLxx?Qr_1K=_x99U-B$qFx zdlz;1cV+gGy@;g4?=0W{BZMh^heV<}Et>jC^GH5fqQYqDX7sf p9kS?HwF4O)3W sjs{Yw zn{Gk&I}rc$ZMjM)kRWvae0H6)|JV-Zmvv?vWch~-w~=9$3^$t$`(YTT;7aZoHW3ns z_QChpK8wk`@MtNYE#Cr=bKx6wovHjffbNY_*w$x-6!|M%
jo2i*sep19Kjj^T3=3<~%Uxf&X$Ipl8fF zw{P3N z;Rj3+&qq5k!X>l1_UW*6s63baAS;IQqt(%#mIGu{3c$E+y zV2pc1$`a?2GNNt)HSE1T8dvBsKBdQ-3MJ7-aN3CzjLgF61Sgfy;=E2J7*P_MFB 3%l9({Ov0aC$X7)LF*n?;J!c!}^ezc{_>M z?~S2r&YN+)B^c5a_>Y8x5qe@147Y`$42qy3-tsY{^JVBopNodN}HqZW*OB)qh ze7IJGl(JI6G!A(&^_BI3WD?F;zFbT@yqkdQCYJZcLbZWpICNueSw|q4Q1GuOuAv*z zo@Bo_j)^-MO)0q0$(T33fH#7E(jr9H@u+G%*qushN-#+#;105}eTQ 7vYt4*Igk(l2|zut5A7;o*JkklYN}0`*C` z8L}V1uF(5H?KRc$2e)6=BW;j>qHu4VA}SS^vc1#e!I1Ut-&40&>GviO %~}X62zt)8O>LbB<-5K6dy^Q&rB1}4UcWc# zRh+G1??4<+Li;ha)qQ17x4yiVI(LZW1idXh)-H9n^T=9FF;@?{Seyx3rM%spw9aUS zDSLLbb+~tI-?>-Ww4=4vwY^2z<7(f!x3-q|upQw?&@r_F*iZPw>asUzOf(huX$aP^ zCnpzbP?Q?)GNqJ$jNOK>PXr3Y3opFeqeQW$D54>R>Q2_} z^^iS1+&696(cbFn5H{gSKIIcJ%@^zm!iR!wvPnzAuRDU 18K zLvPj#eP6=HG%MF2>az)9+MvvO!FM$$0yg@82eqGvJd3D5s?aW^cO$5`mtHEM;3Wju zalPHxvGKUR+}_@|O2k9VL;g+zr79AQ)ZD4XqgW7#(Y~l3kseV~-^dcx Wwg;pBcVZkMfpYJup!=&(jq=M3|&=f zdX~$jfXs&q_G3)&s4o>pXwcnFg7n%4BxH#scP`|Rb!|e^d?K;K%X0M11p$n!V#nnL zaz4n>8zn+1SVW$JsY~%lNnri@0-7!fIa)X}c=6dvDVI{!PXpz{J01F}VMf^#K%i*c zi^ _(a!}5vp6b9aWZX z-_fy|Z}*u0m`lh8F|z{R{j{}XRrE)U`2s!Ke) _+;+ zTf%2KvSS)YV{KEtJjca0%XbGZ @ zw3`h*7lxQ;yaA+#$S(Lw h$8yv OCuxuZctG~B z-AmlCCTGty9RE>1-}>+Q{O$PMkI&=yyoyiRuk-oM_?&nppT8FPz)$k|J74% 3seH{DqS}pU;0CFot^f0p0gfK7SgJihO5=?ap>) z8>p~d{h`ZC4?~vd*WgnMollTp%jNhz1!MF>w^jH|K9|prlkM_~E#-?oWna)=I>a`7 ztnrUmIu?V<^=}0{_)nPYL=c7SCh-{p{V3^+G7fkMpM$9H7sa@6l~>$T)Kb1^xOi*% z>7wF$Y~^Q)wv|tSl*MNfp9}a56?cQXr>uMt Zt_3Y+1q5$eP!txdE lt8GV?Z;R~;&GmDk;KnX9~E`0}mg<3+_MZDr-p6sfR~Tu?kvTvpxy0atky z6ri}VykcW{DgDBHuFQF0&I5BEnDfA#2j)ER2Y7&z7!>(_#&9!7nSB4EeBYvczv6hS z&X?~`lkdyBdl u2rGyj(&x7%DM)2jW*^8omGId!b KN5$WX@RLPe z4vGuPBdLh$*hh4f1@OyODw19&DwgeTn5XmcLpxq%{di)47b%BFba?;JTu! 87O+jgeFDY=91`%bfJX#8D&V+)rv=n&`@jD!-?wS4NxpB> zO7{xFx=s3p%_7hvaQXgC3!4Q5LB5C6s?R48WaRret@<%RkniQR^6wA?BOWxVzy?gRC|Yz#nVyXJS^1cN1&z7^}q}NHl)%iGxR+#Nv;~)1>YcP9$rBenE0= zbk`)ky^M !cvAVxc6p0xv$U#bqrHu=NX^ zkF#1j6=bz|*>n)nQ9Ai!wOXKypL=x${IDi5-A0!;9>+VIxrV$ZEvgg-cmuuRAS~;) zx^cov-N1{J;UE0*ps#?N|6nr3S(;Naep>&Z2^rU=Ve9 IXz%mypgA%teG}gR~A;X8l7#UqZ!@)Gw_1TS3NC7g1Ti+^-~*^~?Rr+WwoM zPd=3TazC>|Y9skh>dSrQ)1Xn4rM}!BC6xOiM0m0K-wP`3HBw*hFA}y3J=uOKCn0U0 zw0B8d?q3qN34Lq(1)1H23-YPdm;0%NazB;zTl;^n(BCKw%KceFxu26h sOmC~aCP-&WW_?8=D%2GY*f=pwMV?hj_yzFH`V%+k!V*e3X+q)> z9Ph%_7X2e1Cn?4x^vFa=CmZ?#W#7bwx&1Q#lThY`Qr^0L9ufL- z{8_V*6!4pP{LWkyGlJ{y;6wFG{X+lGiBz-J|9y-81u@|zJW0k)Ma~~d`Ux&5B@|)9 z$ag3A=n__WiA(qr#Ox-0nGaw1fcn3IE;Uis&t%?4hodOt{CoOKnZD##@8Fx}zSNg_ zpZtGuCPn{g{Zf(bmh &XW4#HcpE3SY;r%AYECvnT{QtmuoG?Ehzin Dk6&l` literal 0 HcmV?d00001 diff --git a/be/algo-with-me-docker/package-lock.json b/be/algo-with-me-docker/package-lock.json deleted file mode 100644 index ad508b5..0000000 --- a/be/algo-with-me-docker/package-lock.json +++ /dev/null @@ -1,760 +0,0 @@ -{ - "name": "algo-with-me-docker", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "express": "^4.18.2" - }, - "devDependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.9.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", - "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - } - } -} diff --git a/be/algo-with-me-docker/package.json b/be/algo-with-me-docker/package.json deleted file mode 100644 index e7d570e..0000000 --- a/be/algo-with-me-docker/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "devDependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.9.0" - }, - "dependencies": { - "express": "^4.18.2" - } -} diff --git a/be/algo-with-me-docker/run.sh b/be/algo-with-me-docker/run.sh deleted file mode 100755 index 8ab3733..0000000 --- a/be/algo-with-me-docker/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -sudo docker run -d \ --p 3000:3000 \ ---name algo-with-me-judge \ -algo-with-me-judge:latest \ No newline at end of file diff --git a/be/algo-with-me-docker/src/app.ts b/be/algo-with-me-docker/src/app.ts deleted file mode 100644 index 70b88b9..0000000 --- a/be/algo-with-me-docker/src/app.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as express from 'express'; - -import fs from 'node:fs'; - -const PORT = process.env.PORT || 3000; -const app = express(); -app.use(express.json()); - -app.get('/', (req, res) => { - res.send('Hello, Docker!'); -}); - -app.post('/', (req, res) => { - const {code, a, b} = req.body; - - const result = eval(`${code} solve(${a}, ${b})`); - - res.send(JSON.stringify(result)); -}) - -app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); -}); diff --git a/be/algo-with-me-docker/tsconfig.json b/be/algo-with-me-docker/tsconfig.json deleted file mode 100644 index f52a0b1..0000000 --- a/be/algo-with-me-docker/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false, - "paths": { - "@src/*": ["./src/*"], - } - } -} From 7799fef380c185a531d0966d16a6ab488653e4e8 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:59:19 +0900 Subject: [PATCH 013/226] =?UTF-8?q?chore:=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=ED=81=90=EB=A5=BC=20=EC=9C=84=ED=95=9C=20bull=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 2 + be/algo-with-me-api/pnpm-lock.yaml | 207 ++++++++++++++++++++++++++++- 2 files changed, 206 insertions(+), 3 deletions(-) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index cdfaf68..bf43f14 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -20,12 +20,14 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@nestjs/bull": "^10.0.1", "@nestjs/common": "^10.2.8", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.0", + "bull": "^4.11.5", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "pg": "^8.11.3", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 2f37c20..0047f64 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@nestjs/bull': + specifier: ^10.0.1 + version: 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(bull@4.11.5) '@nestjs/common': specifier: ^10.2.8 version: 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) @@ -23,6 +26,9 @@ dependencies: '@nestjs/typeorm': specifier: ^10.0.0 version: 10.0.0(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17) + bull: + specifier: ^4.11.5 + version: 4.11.5 class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -583,6 +589,10 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true + /@ioredis/commands@1.2.0: + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + dev: false + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -870,6 +880,79 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: + resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2: + resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2: + resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2: + resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2: + resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2: + resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@nestjs/bull-shared@10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): + resolution: {integrity: sha512-8Td36l2i5x9+iQWjPB5Bd5+6u5Eangb5DclNcwrdwKqvd28xE92MSW97P4JV52C2kxrTjZwx8ck/wObAwtpQPw==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + tslib: 2.6.0 + dev: false + + /@nestjs/bull@10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(bull@4.11.5): + resolution: {integrity: sha512-1GcJ8BkHDgQdBMZ7SqAqgUHiFnISXmpGvewFeTc8wf87JLk2PweiKv9j9/KQKU+NI237pCe82XB0bXzTnsdxSw==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + bull: ^3.3 || ^4.0.0 + dependencies: + '@nestjs/bull-shared': 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + bull: 4.11.5 + tslib: 2.6.0 + dev: false + /@nestjs/cli@10.2.1: resolution: {integrity: sha512-CAJAQwmxFZfB3RTvqz/eaXXWpyU+mZ4QSqfBYzjneTsPgF+uyOAW3yQpaLNn9Dfcv39R9UxSuAhayv6yuFd+Jg==} engines: {node: '>= 16.14'} @@ -2028,6 +2111,21 @@ packages: ieee754: 1.2.1 dev: false + /bull@4.11.5: + resolution: {integrity: sha512-9jazyvBBYr55IRDkfJh/mJjWiq8NJUMoCC5zTuBX4JhkZvVXegnwsaIa1jr3x9xwSxGvWEhwQ9lt1jlCT5j6pQ==} + engines: {node: '>=12'} + dependencies: + cron-parser: 4.9.0 + get-port: 5.1.1 + ioredis: 5.3.2 + lodash: 4.17.21 + msgpackr: 1.9.9 + semver: 7.5.4 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + dev: false + /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -2195,6 +2293,11 @@ packages: engines: {node: '>=0.8'} dev: true + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -2343,6 +2446,13 @@ packages: /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + /cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} + dependencies: + luxon: 3.4.4 + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2460,6 +2570,11 @@ packages: engines: {node: '>=0.4.0'} dev: true + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: false + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3258,6 +3373,11 @@ packages: engines: {node: '>=8.0.0'} dev: true + /get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + dev: false + /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -3573,6 +3693,23 @@ packages: engines: {node: '>= 0.10'} dev: true + /ioredis@5.3.2: + resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} + engines: {node: '>=12.22.0'} + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.4 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4382,6 +4519,14 @@ packages: p-locate: 5.0.0 dev: true + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: false + + /lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + dev: false + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -4419,7 +4564,11 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true + + /luxon@3.4.4: + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} + dev: false /macos-release@2.5.1: resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} @@ -4576,6 +4725,28 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + /msgpackr-extract@3.0.2: + resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} + hasBin: true + requiresBuild: true + dependencies: + node-gyp-build-optional-packages: 5.0.7 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2 + dev: false + optional: true + + /msgpackr@1.9.9: + resolution: {integrity: sha512-sbn6mioS2w0lq1O6PpGtsv6Gy8roWM+o3o4Sqjd6DudrL/nOugY+KyJUimoWzHnf9OkO0T6broHFnYE/R05t9A==} + optionalDependencies: + msgpackr-extract: 3.0.2 + dev: false + /multer@1.4.4-lts.1: resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} engines: {node: '>= 6.0.0'} @@ -4633,6 +4804,13 @@ packages: dependencies: whatwg-url: 5.0.0 + /node-gyp-build-optional-packages@5.0.7: + resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} + hasBin: true + requiresBuild: true + dev: false + optional: true + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -5155,6 +5333,18 @@ packages: resolve: 1.22.8 dev: true + /redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + dev: false + + /redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: false + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} @@ -5318,7 +5508,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -5473,6 +5662,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: false + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -5876,6 +6069,10 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true + /tslib@2.6.0: + resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + dev: false + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -6089,6 +6286,11 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} hasBin: true @@ -6285,7 +6487,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} From 1b8432114506c75729e5eb8fe993622451414bb3 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:09:16 +0900 Subject: [PATCH 014/226] =?UTF-8?q?chore:=20=ED=8F=B4=EB=8D=94=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/competition/competition.module.ts | 8 ++++---- .../{ => controllers}/competition.controller.ts | 2 +- .../competition/{ => controllers}/problem.controller.ts | 4 ++-- .../src/competition/{ => services}/competition.service.ts | 2 +- .../src/competition/{ => services}/problem.service.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) rename be/algo-with-me-api/src/competition/{ => controllers}/competition.controller.ts (82%) rename be/algo-with-me-api/src/competition/{ => controllers}/problem.controller.ts (87%) rename be/algo-with-me-api/src/competition/{ => services}/competition.service.ts (95%) rename be/algo-with-me-api/src/competition/{ => services}/problem.service.ts (93%) diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 75197a5..4d90521 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { CompetitionController } from './competition.controller'; -import { CompetitionService } from './competition.service'; +import { CompetitionController } from './controllers/competition.controller'; +import { ProblemController } from './controllers/problem.controller'; import { Problem } from './entities/problem.entity'; -import { ProblemController } from './problem.controller'; -import { ProblemService } from './problem.service'; +import { CompetitionService } from './services/competition.service'; +import { ProblemService } from './services/problem.service'; @Module({ imports: [TypeOrmModule.forFeature([Problem])], diff --git a/be/algo-with-me-api/src/competition/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts similarity index 82% rename from be/algo-with-me-api/src/competition/competition.controller.ts rename to be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 8dd928d..b076e58 100644 --- a/be/algo-with-me-api/src/competition/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Param } from '@nestjs/common'; -import { CompetitionService } from './competition.service'; +import { CompetitionService } from '../services/competition.service'; @Controller('competitions') export class CompetitionController { diff --git a/be/algo-with-me-api/src/competition/problem.controller.ts b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts similarity index 87% rename from be/algo-with-me-api/src/competition/problem.controller.ts rename to be/algo-with-me-api/src/competition/controllers/problem.controller.ts index c926d12..0d7a267 100644 --- a/be/algo-with-me-api/src/competition/problem.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts @@ -9,8 +9,8 @@ import { Delete, } from '@nestjs/common'; -import { CreateProblemDto } from './dto/create-problem.dto'; -import { ProblemService } from './problem.service'; +import { CreateProblemDto } from '../dto/create-problem.dto'; +import { ProblemService } from '../services/problem.service'; @Controller('problems') export class ProblemController { diff --git a/be/algo-with-me-api/src/competition/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts similarity index 95% rename from be/algo-with-me-api/src/competition/competition.service.ts rename to be/algo-with-me-api/src/competition/services/competition.service.ts index 8cc3d2a..155ed71 100644 --- a/be/algo-with-me-api/src/competition/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -5,7 +5,7 @@ import { Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; -import { Problem } from './entities/problem.entity'; +import { Problem } from '../entities/problem.entity'; @Injectable() export class CompetitionService { diff --git a/be/algo-with-me-api/src/competition/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts similarity index 93% rename from be/algo-with-me-api/src/competition/problem.service.ts rename to be/algo-with-me-api/src/competition/services/problem.service.ts index f4dfb8a..ee50ecb 100644 --- a/be/algo-with-me-api/src/competition/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -5,8 +5,8 @@ import { Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; -import { CreateProblemDto } from './dto/create-problem.dto'; -import { Problem } from './entities/problem.entity'; +import { CreateProblemDto } from '../dto/create-problem.dto'; +import { Problem } from '../entities/problem.entity'; @Injectable() export class ProblemService { From f35dee5f849518a8ae66d5ab5f4eece417e3e8d8 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:46:34 +0900 Subject: [PATCH 015/226] =?UTF-8?q?feat:=20bull=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20redis=20=ED=81=90=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 7 +++++++ be/algo-with-me-api/src/competition/competition.module.ts | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index e4d68c9..0292517 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -1,3 +1,4 @@ +import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; @@ -21,6 +22,12 @@ import { Problem } from './competition/entities/problem.entity'; synchronize: true, entities: [Problem], }), + BullModule.forRoot({ + redis: { + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT), + }, + }), CompetitionModule, ], }) diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 4d90521..fbac89c 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -1,3 +1,4 @@ +import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; @@ -8,7 +9,12 @@ import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; @Module({ - imports: [TypeOrmModule.forFeature([Problem])], + imports: [ + TypeOrmModule.forFeature([Problem]), + BullModule.registerQueue({ + name: process.env.REDIS_MESSAGE_QUEU_NAME, + }), + ], controllers: [ProblemController, CompetitionController], providers: [ProblemService, CompetitionService], }) From a36456bb4c25951a3b95a8b5d86b6e30b0f2dc57 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:08:59 +0900 Subject: [PATCH 016/226] =?UTF-8?q?chore:=20redis=20produce,=20consume=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 1 + .../src/competition/competition.module.ts | 5 ++-- .../src/competition/controllers/consumer.ts | 25 +++++++++++++++++++ .../services/competition.service.ts | 10 +++++++- 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/controllers/consumer.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 0292517..8cd2c1a 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -26,6 +26,7 @@ import { Problem } from './competition/entities/problem.entity'; redis: { host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, }, }), CompetitionModule, diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index fbac89c..4c77439 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -3,6 +3,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { CompetitionController } from './controllers/competition.controller'; +import { SubmissionConsumer } from './controllers/consumer'; import { ProblemController } from './controllers/problem.controller'; import { Problem } from './entities/problem.entity'; import { CompetitionService } from './services/competition.service'; @@ -12,10 +13,10 @@ import { ProblemService } from './services/problem.service'; imports: [ TypeOrmModule.forFeature([Problem]), BullModule.registerQueue({ - name: process.env.REDIS_MESSAGE_QUEU_NAME, + name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), ], controllers: [ProblemController, CompetitionController], - providers: [ProblemService, CompetitionService], + providers: [ProblemService, CompetitionService, SubmissionConsumer], }) export class CompetitionModule {} diff --git a/be/algo-with-me-api/src/competition/controllers/consumer.ts b/be/algo-with-me-api/src/competition/controllers/consumer.ts new file mode 100644 index 0000000..3e409de --- /dev/null +++ b/be/algo-with-me-api/src/competition/controllers/consumer.ts @@ -0,0 +1,25 @@ +import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; + +@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +export class SubmissionConsumer { + @Process() + async transcode(job: Job ) { + console.log(job.data); + for (let i = 0; i < 10; i++) { + console.log(i); + } + // console.log(await job.progress(100)); + // console.log(await job.moveToCompleted()); + // console.log(await job.remove()); + return { good: 'good' }; + } + + @OnQueueCompleted() + async onCompleted(job: Job , result: any) { + await job.remove(); + console.log('job done'); + console.log(job); + console.log(result); + } +} diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 155ed71..ebb1f73 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,5 +1,7 @@ +import { InjectQueue } from '@nestjs/bull'; import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { Queue } from 'bull'; import { Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; @@ -9,9 +11,15 @@ import { Problem } from '../entities/problem.entity'; @Injectable() export class CompetitionService { - constructor(@InjectRepository(Problem) private readonly problemRepository: Repository ) {} + constructor( + @InjectRepository(Problem) private readonly problemRepository: Repository , + @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, + ) {} async findOneProblem(id: number) { + await this.submissionQueue.add({ + test: 'test', + }); const problem = await this.problemRepository.findOneBy({ id }); const fileName = id.toString() + '.md'; const paths = path.join(process.env.PROBLEM_PATH, id.toString(), fileName); From 6027b9c7e637ab2a20cf9c6e0ae9b710c491ff3b Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:00:28 +0900 Subject: [PATCH 017/226] =?UTF-8?q?feat:=20=EC=A0=9C=EC=B6=9C=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 3 +- .../src/competition/competition.enums.ts | 7 ++++ .../src/competition/competition.module.ts | 6 +-- .../src/competition/controllers/consumer.ts | 25 ------------ .../competition/entities/problem.entity.ts | 6 +++ .../competition/entities/submission.entity.ts | 39 +++++++++++++++++++ 6 files changed, 57 insertions(+), 29 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/competition.enums.ts delete mode 100644 be/algo-with-me-api/src/competition/controllers/consumer.ts create mode 100644 be/algo-with-me-api/src/competition/entities/submission.entity.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 8cd2c1a..481626a 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -5,6 +5,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CompetitionModule } from './competition/competition.module'; import { Problem } from './competition/entities/problem.entity'; +import { Submission } from './competition/entities/submission.entity'; @Module({ imports: [ @@ -20,7 +21,7 @@ import { Problem } from './competition/entities/problem.entity'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem], + entities: [Problem, Submission], }), BullModule.forRoot({ redis: { diff --git a/be/algo-with-me-api/src/competition/competition.enums.ts b/be/algo-with-me-api/src/competition/competition.enums.ts new file mode 100644 index 0000000..03836b2 --- /dev/null +++ b/be/algo-with-me-api/src/competition/competition.enums.ts @@ -0,0 +1,7 @@ +export const RESULT = { + PROGRESS: '처리중', + CORRECT: '정답입니다', + WRONG: '오답입니다.', + TIMEOUT: '시간초과', + OOM: '메모리초과', +} as const; diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 4c77439..359e50a 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -3,20 +3,20 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { CompetitionController } from './controllers/competition.controller'; -import { SubmissionConsumer } from './controllers/consumer'; import { ProblemController } from './controllers/problem.controller'; import { Problem } from './entities/problem.entity'; +import { Submission } from './entities/submission.entity'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; @Module({ imports: [ - TypeOrmModule.forFeature([Problem]), + TypeOrmModule.forFeature([Problem, Submission]), BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), ], controllers: [ProblemController, CompetitionController], - providers: [ProblemService, CompetitionService, SubmissionConsumer], + providers: [ProblemService, CompetitionService], }) export class CompetitionModule {} diff --git a/be/algo-with-me-api/src/competition/controllers/consumer.ts b/be/algo-with-me-api/src/competition/controllers/consumer.ts deleted file mode 100644 index 3e409de..0000000 --- a/be/algo-with-me-api/src/competition/controllers/consumer.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; -import { Job } from 'bull'; - -@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) -export class SubmissionConsumer { - @Process() - async transcode(job: Job ) { - console.log(job.data); - for (let i = 0; i < 10; i++) { - console.log(i); - } - // console.log(await job.progress(100)); - // console.log(await job.moveToCompleted()); - // console.log(await job.remove()); - return { good: 'good' }; - } - - @OnQueueCompleted() - async onCompleted(job: Job , result: any) { - await job.remove(); - console.log('job done'); - console.log(job); - console.log(result); - } -} diff --git a/be/algo-with-me-api/src/competition/entities/problem.entity.ts b/be/algo-with-me-api/src/competition/entities/problem.entity.ts index 381d293..1783663 100644 --- a/be/algo-with-me-api/src/competition/entities/problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/problem.entity.ts @@ -2,10 +2,13 @@ import { Column, CreateDateColumn, Entity, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { Submission } from './submission.entity'; + @Entity() export class Problem { @PrimaryGeneratedColumn() @@ -29,6 +32,9 @@ export class Problem { @Column('text') solutionCode: string; + @OneToMany(() => Submission, (submission) => submission.problem) + submissions: Submission[]; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts new file mode 100644 index 0000000..d9e97da --- /dev/null +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -0,0 +1,39 @@ +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { Problem } from './problem.entity'; +import { RESULT } from '../competition.enums'; + +@Entity() +export class Submission { + @PrimaryGeneratedColumn() + id: number; + + @Column('text') + code: string; + + @Column({ + type: 'enum', + enum: RESULT, + default: RESULT.PROGRESS, + }) + result: string; + + @Column('json', { nullable: true }) + detail: string; + + @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) + problem: Problem; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} From 59e7e8bd3d3d29b0c1f1a5c881b49af9e6d78ceb Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:26:37 +0900 Subject: [PATCH 018/226] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EC=B6=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드 제출시 db에 저장 및 큐에 데이터 삽입하는 로직 작성 --- .../competition/dto/create-submission.dto.ts | 19 +++++++++++++++++++ .../services/competition.service.ts | 16 ++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 be/algo-with-me-api/src/competition/dto/create-submission.dto.ts diff --git a/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts b/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts new file mode 100644 index 0000000..f19123a --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts @@ -0,0 +1,19 @@ +import { IsNotEmpty } from 'class-validator'; + +import { Problem } from '../entities/problem.entity'; +import { Submission } from '../entities/submission.entity'; + +export class CreateSubmissionDto { + @IsNotEmpty() + problemId: number; + + @IsNotEmpty() + code: string; + + toEntity(problem: Problem): Submission { + const submission = new Submission(); + submission.problem = problem; + submission.code = this.code; + return submission; + } +} diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index ebb1f73..5e5dd6e 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -7,12 +7,15 @@ import { Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; +import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { Problem } from '../entities/problem.entity'; +import { Submission } from '../entities/submission.entity'; @Injectable() export class CompetitionService { constructor( @InjectRepository(Problem) private readonly problemRepository: Repository , + @InjectRepository(Submission) private readonly submissionRepository: Repository , @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, ) {} @@ -36,4 +39,17 @@ export class CompetitionService { createdAt: problem.createdAt, }; } + + async scoreSubmission(createSubmissionDto: CreateSubmissionDto) { + const problem: Problem = await this.problemRepository.findOneBy({ + id: createSubmissionDto.problemId, + }); + const submission: Submission = createSubmissionDto.toEntity(problem); + const savedSubmission: Submission = await this.submissionRepository.save(submission); + await this.submissionQueue.add({ + problemId: savedSubmission.problem.id, + }); + + return savedSubmission; + } } From f88aeb83647abccbc9aae8c7fa2a80d7c04fc2ca Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:31:19 +0900 Subject: [PATCH 019/226] =?UTF-8?q?feat:=20=EC=A0=9C=EC=B6=9C=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84,=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20=EC=BB=A8=EC=8A=88=EB=A8=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/competition.module.ts | 3 ++- .../controllers/competition.controller.ts | 9 +++++++- .../src/competition/tem.consumer.ts | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/tem.consumer.ts diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 359e50a..19a060f 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -8,6 +8,7 @@ import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; +import { SubmissionConsumer } from './tem.consumer'; @Module({ imports: [ @@ -17,6 +18,6 @@ import { ProblemService } from './services/problem.service'; }), ], controllers: [ProblemController, CompetitionController], - providers: [ProblemService, CompetitionService], + providers: [ProblemService, CompetitionService, SubmissionConsumer], }) export class CompetitionModule {} diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index b076e58..9fdae57 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,5 +1,6 @@ -import { Controller, Get, Param } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, UsePipes, ValidationPipe } from '@nestjs/common'; +import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { CompetitionService } from '../services/competition.service'; @Controller('competitions') @@ -10,4 +11,10 @@ export class CompetitionController { findOne(@Param('id') id: number) { return this.competitionService.findOneProblem(id); } + + @Post('submissions') + @UsePipes(new ValidationPipe({ transform: true })) + createSubmission(@Body() createSubmissionDto: CreateSubmissionDto) { + return this.competitionService.scoreSubmission(createSubmissionDto); + } } diff --git a/be/algo-with-me-api/src/competition/tem.consumer.ts b/be/algo-with-me-api/src/competition/tem.consumer.ts new file mode 100644 index 0000000..50ba961 --- /dev/null +++ b/be/algo-with-me-api/src/competition/tem.consumer.ts @@ -0,0 +1,22 @@ +import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; + +@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +export class SubmissionConsumer { + @Process() + async transcode(job: Job ) { + console.log(job.data); + for (let i = 0; i < 10; i++) { + console.log(i); + } + return { good: 'good' }; + } + + @OnQueueCompleted() + onCompleted(job: Job , result: any) { + console.log('job done'); + console.log(result); + // redis 에서 데이터 삭제 + job.remove(); + } +} From dca5aa50a750180e16a321328940211e16766a4f Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:05:55 +0900 Subject: [PATCH 020/226] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20queue=20=EC=82=BD=EC=9E=85=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C,=20queue=20=EB=93=A4=EC=96=B4=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=EA=B0=92=EC=97=90=20submissionId=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/services/competition.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 5e5dd6e..f156a5f 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -20,9 +20,6 @@ export class CompetitionService { ) {} async findOneProblem(id: number) { - await this.submissionQueue.add({ - test: 'test', - }); const problem = await this.problemRepository.findOneBy({ id }); const fileName = id.toString() + '.md'; const paths = path.join(process.env.PROBLEM_PATH, id.toString(), fileName); @@ -48,6 +45,7 @@ export class CompetitionService { const savedSubmission: Submission = await this.submissionRepository.save(submission); await this.submissionQueue.add({ problemId: savedSubmission.problem.id, + submissionId: savedSubmission.id, }); return savedSubmission; From 1759779e4698748cbcac0c3476b726ebffb4b5e6 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 13:57:11 +0900 Subject: [PATCH 021/226] =?UTF-8?q?chore:=20docker=20image=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20algo-with-me-judge=EC=97=90=EC=84=9C=20alg?= =?UTF-8?q?o-with-me-docker=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/Dockerfile | 1 - be/algo-with-me-docker/build.sh | 2 +- be/algo-with-me-docker/docker-sh/prune.sh | 2 +- be/algo-with-me-docker/docker-sh/run.sh | 2 +- be/algo-with-me-docker/docker-sh/stop.sh | 4 ++-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index 40348d5..70b4378 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -2,7 +2,6 @@ ARG NODE_VERSION=20.9.0 ARG ALPINE_VERSION=3.18 FROM node:${NODE_VERSION}-alpine -MAINTAINER Yechan Lee CMD "mkdir -p /algo-with-me" diff --git a/be/algo-with-me-docker/build.sh b/be/algo-with-me-docker/build.sh index 2065b3b..b13aa90 100755 --- a/be/algo-with-me-docker/build.sh +++ b/be/algo-with-me-docker/build.sh @@ -1 +1 @@ -sudo docker build . -t algo-with-me-judge:latest \ No newline at end of file +sudo docker build . -t algo-with-me-docker:latest \ No newline at end of file diff --git a/be/algo-with-me-docker/docker-sh/prune.sh b/be/algo-with-me-docker/docker-sh/prune.sh index f1caedd..d6ae348 100755 --- a/be/algo-with-me-docker/docker-sh/prune.sh +++ b/be/algo-with-me-docker/docker-sh/prune.sh @@ -1,3 +1,3 @@ -sudo docker container stop algo-with-me-judge +sudo docker container stop algo-with-me-docker sudo docker container prune --force sudo docker image prune --force diff --git a/be/algo-with-me-docker/docker-sh/run.sh b/be/algo-with-me-docker/docker-sh/run.sh index da553f0..92934da 100755 --- a/be/algo-with-me-docker/docker-sh/run.sh +++ b/be/algo-with-me-docker/docker-sh/run.sh @@ -8,7 +8,7 @@ sudo docker run \ -v $HOME/algo-with-me/submissions:/algo-with-me/submissions \ --user $(id -u):$(id -g) \ --name algo-with-me-judge \ -algo-with-me-judge:latest +algo-with-me-docker:latest #--network none \ #--network isolatedNetwork \ \ No newline at end of file diff --git a/be/algo-with-me-docker/docker-sh/stop.sh b/be/algo-with-me-docker/docker-sh/stop.sh index 759be09..e3e2059 100755 --- a/be/algo-with-me-docker/docker-sh/stop.sh +++ b/be/algo-with-me-docker/docker-sh/stop.sh @@ -1,4 +1,4 @@ echo "stopped container: " -sudo docker container stop algo-with-me-judge +sudo docker container stop algo-with-me-docker echo "removed container: " -sudo docker container rm algo-with-me-judge \ No newline at end of file +sudo docker container rm algo-with-me-docker \ No newline at end of file From dba0280227fc9395b1182df4e87be9b5ff1eaf9b Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 14:19:24 +0900 Subject: [PATCH 022/226] =?UTF-8?q?chore:=20express=20pnpm=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/.dockerignore | 8 +- be/algo-with-me-docker/.eslintrc.js | 62 +++ be/algo-with-me-docker/.gitignore | 131 +++++++ be/algo-with-me-docker/.prettierrc | 8 + be/algo-with-me-docker/Dockerfile | 8 +- be/algo-with-me-docker/package.json | 8 + be/algo-with-me-docker/pnpm-lock.yaml | 531 ++++++++++++++++++++++++++ be/algo-with-me-docker/src/app.ts | 12 + be/algo-with-me-docker/tsconfig.json | 24 ++ 9 files changed, 789 insertions(+), 3 deletions(-) create mode 100644 be/algo-with-me-docker/.eslintrc.js create mode 100644 be/algo-with-me-docker/.gitignore create mode 100644 be/algo-with-me-docker/.prettierrc create mode 100644 be/algo-with-me-docker/package.json create mode 100644 be/algo-with-me-docker/pnpm-lock.yaml create mode 100644 be/algo-with-me-docker/src/app.ts create mode 100644 be/algo-with-me-docker/tsconfig.json diff --git a/be/algo-with-me-docker/.dockerignore b/be/algo-with-me-docker/.dockerignore index 4921d12..0823358 100644 --- a/be/algo-with-me-docker/.dockerignore +++ b/be/algo-with-me-docker/.dockerignore @@ -1,2 +1,8 @@ +node-modules/ docker-sh/ -build.sh \ No newline at end of file +src/ +build.sh +.eslintrc.js +.prettierrc +.gitignore +tsconfig.json \ No newline at end of file diff --git a/be/algo-with-me-docker/.eslintrc.js b/be/algo-with-me-docker/.eslintrc.js new file mode 100644 index 0000000..2717b87 --- /dev/null +++ b/be/algo-with-me-docker/.eslintrc.js @@ -0,0 +1,62 @@ +module.exports = { + extends: [ + 'plugin:@typescript-eslint/recommended', + // nestjs 스타일 가이드 + 'plugin:nestjs/recommended', + // google 스타일 가이드 + // 'google', + // import sort 관련 설정 + 'plugin:import/recommended', + 'plugin:import/typescript', + // prettier + 'prettier', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin', 'nestjs'], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + // import sort 관련 설정 + 'import/order': [ + 'error', + { + groups: ['external', 'builtin', ['parent', 'sibling'], 'internal'], + pathGroups: [ + { + pattern: 'nest', + group: 'external', + position: 'before', + }, + ], + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + 'newlines-between': 'always', + }, + ], + // + }, + settings: { + // import sort 관련 설정 + 'import/resolver': { + typescript: {}, + node: { + paths: ['src'], + }, + }, + }, +}; diff --git a/be/algo-with-me-docker/.gitignore b/be/algo-with-me-docker/.gitignore new file mode 100644 index 0000000..62d1323 --- /dev/null +++ b/be/algo-with-me-docker/.gitignore @@ -0,0 +1,131 @@ +### Node template +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/be/algo-with-me-docker/.prettierrc b/be/algo-with-me-docker/.prettierrc new file mode 100644 index 0000000..5736e29 --- /dev/null +++ b/be/algo-with-me-docker/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 100, + "useTabs": false, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "all", + "semi": true +} diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index 70b4378..8694100 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -1,13 +1,17 @@ ARG NODE_VERSION=20.9.0 ARG ALPINE_VERSION=3.18 -FROM node:${NODE_VERSION}-alpine +FROM node:${NODE_VERSION}-alpine AS base CMD "mkdir -p /algo-with-me" - WORKDIR /algo-with-me COPY --chmod=555 ./node-sh /algo-with-me/node-sh +RUN apk update \ +# && apk add --no-cache libc6-compat \ + && npm install -g pnpm \ + && pnpm install --prod + # EXPOSE 3000 CMD ./node-sh/run.sh $COMPETITION_ID $USER_ID $PROBLEM_ID diff --git a/be/algo-with-me-docker/package.json b/be/algo-with-me-docker/package.json new file mode 100644 index 0000000..56066c9 --- /dev/null +++ b/be/algo-with-me-docker/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "express": "^4.18.2" + }, + "devDependencies": { + "@types/express": "^4.17.21" + } +} diff --git a/be/algo-with-me-docker/pnpm-lock.yaml b/be/algo-with-me-docker/pnpm-lock.yaml new file mode 100644 index 0000000..83c8e46 --- /dev/null +++ b/be/algo-with-me-docker/pnpm-lock.yaml @@ -0,0 +1,531 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + express: + specifier: ^4.18.2 + version: 4.18.2 + +devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + +packages: + + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.9.0 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.9.0 + dev: true + + /@types/express-serve-static-core@4.17.41: + resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} + dependencies: + '@types/node': 20.9.0 + '@types/qs': 6.9.10 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.41 + '@types/qs': 6.9.10 + '@types/serve-static': 1.15.5 + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + + /@types/node@20.9.0: + resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/qs@6.9.10: + resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.9.0 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.9.0 + dev: true + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: false + + /body-parser@1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + dev: false + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + + /express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: false + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: false + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: false + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /raw-body@2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: false + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false diff --git a/be/algo-with-me-docker/src/app.ts b/be/algo-with-me-docker/src/app.ts new file mode 100644 index 0000000..a3bd56b --- /dev/null +++ b/be/algo-with-me-docker/src/app.ts @@ -0,0 +1,12 @@ +import express from 'express'; + +const app = express() +const port = 3000 + +app.post('/', (req, res) => { + res.send('Hello World!'); +}) + +app.listen(port, () => { + console.log(`[algo-with-me-docker] listening at port ${port}`); +}) diff --git a/be/algo-with-me-docker/tsconfig.json b/be/algo-with-me-docker/tsconfig.json new file mode 100644 index 0000000..5627a41 --- /dev/null +++ b/be/algo-with-me-docker/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "paths": { + "@src/*": ["./src/*"] + } + } +} From 8d65d14972faed4b0757909fb746c87e074a4def Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 14:27:38 +0900 Subject: [PATCH 023/226] =?UTF-8?q?fix:=20Dockerfile=20=EB=B9=8C=EB=93=9C?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/.dockerignore | 1 - be/algo-with-me-docker/Dockerfile | 4 ++-- be/algo-with-me-docker/docker-sh/run.sh | 2 +- be/algo-with-me-docker/node-sh/run.sh | 3 ++- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/be/algo-with-me-docker/.dockerignore b/be/algo-with-me-docker/.dockerignore index 0823358..4fe7702 100644 --- a/be/algo-with-me-docker/.dockerignore +++ b/be/algo-with-me-docker/.dockerignore @@ -1,5 +1,4 @@ node-modules/ -docker-sh/ src/ build.sh .eslintrc.js diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index 8694100..d1960d6 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -6,11 +6,11 @@ FROM node:${NODE_VERSION}-alpine AS base CMD "mkdir -p /algo-with-me" WORKDIR /algo-with-me COPY --chmod=555 ./node-sh /algo-with-me/node-sh +COPY . /algo-with-me RUN apk update \ -# && apk add --no-cache libc6-compat \ && npm install -g pnpm \ - && pnpm install --prod + && pnpm install # EXPOSE 3000 diff --git a/be/algo-with-me-docker/docker-sh/run.sh b/be/algo-with-me-docker/docker-sh/run.sh index 92934da..c414240 100755 --- a/be/algo-with-me-docker/docker-sh/run.sh +++ b/be/algo-with-me-docker/docker-sh/run.sh @@ -7,7 +7,7 @@ sudo docker run \ -v $HOME/algo-with-me/testcases:/algo-with-me/testcases:ro \ -v $HOME/algo-with-me/submissions:/algo-with-me/submissions \ --user $(id -u):$(id -g) \ ---name algo-with-me-judge \ +--name algo-with-me-docker \ algo-with-me-docker:latest #--network none \ diff --git a/be/algo-with-me-docker/node-sh/run.sh b/be/algo-with-me-docker/node-sh/run.sh index 96b6914..87ae896 100755 --- a/be/algo-with-me-docker/node-sh/run.sh +++ b/be/algo-with-me-docker/node-sh/run.sh @@ -21,7 +21,8 @@ if [ -f "$SUBMISSION_JS_FILE" ]; then # U Total number of CPU-seconds that the process used directly (in user mode), in seconds. # e Elapsed real (wall clock) time used by the process, in seconds. # M Maximum resident set size of the process during its lifetime, in Kilobytes. - /usr/bin/time -o "$DETAIL_FILE" -f "%e %M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" + # /usr/bin/time -o "$DETAIL_FILE" -f "%e %M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" + /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" echo "[algo-with-me] run.sh: successfully ran $SUBMISSION_JS_FILE" else echo "[algo-with-me] run.sh: cannot find submitted js file $SUBMISSION_JS_FILE" From 0a8914332733404547526801dbb4a16832fac278 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 15:08:12 +0900 Subject: [PATCH 024/226] =?UTF-8?q?feat:=20docker=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=20api=20=EB=A7=8C=EB=93=A6.=20Dockerfile?= =?UTF-8?q?=EC=9D=98=20entrypoint=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/Dockerfile | 5 +++-- be/algo-with-me-docker/node-sh/run.sh | 7 ++++--- be/algo-with-me-docker/node-sh/runJs.sh | 2 +- be/algo-with-me-docker/src/app.ts | 21 +++++++++++++++++++-- be/algo-with-me-docker/tsconfig.json | 1 + 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index d1960d6..f8f8655 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -1,5 +1,6 @@ ARG NODE_VERSION=20.9.0 ARG ALPINE_VERSION=3.18 +ARG PORT=3000 FROM node:${NODE_VERSION}-alpine AS base @@ -12,6 +13,6 @@ RUN apk update \ && npm install -g pnpm \ && pnpm install -# EXPOSE 3000 +EXPOSE ${PORT} -CMD ./node-sh/run.sh $COMPETITION_ID $USER_ID $PROBLEM_ID +CMD node dist/app.js diff --git a/be/algo-with-me-docker/node-sh/run.sh b/be/algo-with-me-docker/node-sh/run.sh index 87ae896..62948f0 100755 --- a/be/algo-with-me-docker/node-sh/run.sh +++ b/be/algo-with-me-docker/node-sh/run.sh @@ -7,7 +7,7 @@ # SUBMISSION_JS_FILE에서 파일을 읽어, node로 실행한다. # DETAIL_FILE에는 사용한 시간(sec)과 최대 memory 메모리 사용량(KB)이 공백(' ')으로 분리되어 기록된다. -mkdir -p "/algo-with-me/submissions/$1/$2/" +mkdir -p "/algo-with-me/submissions/$1/$2/" || exit 1 SUBMISSION_JS_FILE="/algo-with-me/submissions/$1/$2/$3.js" DETAIL_FILE="/algo-with-me/submissions/$1/$2/$3.detail" @@ -22,10 +22,11 @@ if [ -f "$SUBMISSION_JS_FILE" ]; then # e Elapsed real (wall clock) time used by the process, in seconds. # M Maximum resident set size of the process during its lifetime, in Kilobytes. # /usr/bin/time -o "$DETAIL_FILE" -f "%e %M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" - /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" + /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" || exit 2 echo "[algo-with-me] run.sh: successfully ran $SUBMISSION_JS_FILE" else echo "[algo-with-me] run.sh: cannot find submitted js file $SUBMISSION_JS_FILE" + exit 3 fi -exit +exit 0 diff --git a/be/algo-with-me-docker/node-sh/runJs.sh b/be/algo-with-me-docker/node-sh/runJs.sh index 7d67e73..4afe4a8 100755 --- a/be/algo-with-me-docker/node-sh/runJs.sh +++ b/be/algo-with-me-docker/node-sh/runJs.sh @@ -16,4 +16,4 @@ RESULT_FILE="/algo-with-me/submissions/$1/$2/$3.result" node "$SUBMISSION_JS_FILE" "$RESULT_FILE" 1> "$STDOUT_FILE" 2> "$STDERR_FILE" -exit +exit 0 diff --git a/be/algo-with-me-docker/src/app.ts b/be/algo-with-me-docker/src/app.ts index a3bd56b..3a0bad8 100644 --- a/be/algo-with-me-docker/src/app.ts +++ b/be/algo-with-me-docker/src/app.ts @@ -1,10 +1,27 @@ import express from 'express'; +import child_process from 'node:child_process'; + const app = express() const port = 3000 -app.post('/', (req, res) => { - res.send('Hello World!'); +function execute(cmd) { + try { + child_process.execSync(cmd); + return true; + } + catch (error) { + return false; + } +}; + +app.post('/:competitionId/:userId/:problemId', (req, res) => { + const {competitionId, userId, problemId} = req.params; + // TODO: 10초 timeout 만들기 + // const result = execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId}`); + execute(`./node-sh/run.sh ${competitionId} ${userId} ${problemId}`) ? + res.send(JSON.stringify({result: 'SUCCESS', competitionId, userId, problemId})) : + res.send(JSON.stringify({result: 'FAIL', competitionId, userId, problemId})); }) app.listen(port, () => { diff --git a/be/algo-with-me-docker/tsconfig.json b/be/algo-with-me-docker/tsconfig.json index 5627a41..c7f3c12 100644 --- a/be/algo-with-me-docker/tsconfig.json +++ b/be/algo-with-me-docker/tsconfig.json @@ -17,6 +17,7 @@ "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false, + "esModuleInterop": true, // https://stackoverflow.com/questions/63744824/getting-express-default-is-not-a-function-error-when-i-run-node-server-in-a-co "paths": { "@src/*": ["./src/*"] } From 2bbf80a52ccf175966928a7809b9cac8a79a3cdb Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 15:59:56 +0900 Subject: [PATCH 025/226] =?UTF-8?q?feat:=20timeout=2010=EC=B4=88=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/docker-sh/run.sh | 4 +- be/algo-with-me-docker/src/app.ts | 53 ++++++++++++++++--------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/be/algo-with-me-docker/docker-sh/run.sh b/be/algo-with-me-docker/docker-sh/run.sh index c414240..37bc9e0 100755 --- a/be/algo-with-me-docker/docker-sh/run.sh +++ b/be/algo-with-me-docker/docker-sh/run.sh @@ -1,4 +1,4 @@ -sudo docker run \ +sudo docker run -d \ -p 3000:3000 \ -e COMPETITION_ID=$1 \ -e USER_ID=$2 \ @@ -6,7 +6,7 @@ sudo docker run \ -v $HOME/algo-with-me/problems:/algo-with-me/problems:ro \ -v $HOME/algo-with-me/testcases:/algo-with-me/testcases:ro \ -v $HOME/algo-with-me/submissions:/algo-with-me/submissions \ ---user $(id -u):$(id -g) \ +--user "$(id -u)":"$(id -g)" \ --name algo-with-me-docker \ algo-with-me-docker:latest diff --git a/be/algo-with-me-docker/src/app.ts b/be/algo-with-me-docker/src/app.ts index 3a0bad8..8909a18 100644 --- a/be/algo-with-me-docker/src/app.ts +++ b/be/algo-with-me-docker/src/app.ts @@ -2,28 +2,45 @@ import express from 'express'; import child_process from 'node:child_process'; -const app = express() -const port = 3000 +const app = express(); +const PORT = 3000; +const TIMEOUT_IN_MILLI = 10_000; -function execute(cmd) { - try { - child_process.execSync(cmd); - return true; - } - catch (error) { - return false; - } -}; +function execute(cmd: string) { + return new Promise ((resolve, reject) => { + child_process.exec(cmd, (err, stdout, stderr) => { + if (err) { + reject(new Error(stderr)); + } + resolve(true); + }) + }); +} + +function getTimer(timeoutInMilli: number): Promise { + return new Promise((resolve, reject) => { + setTimeout(() => {reject(new Error('TIMEOUT'));}, timeoutInMilli); + }) +} app.post('/:competitionId/:userId/:problemId', (req, res) => { const {competitionId, userId, problemId} = req.params; - // TODO: 10초 timeout 만들기 - // const result = execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId}`); - execute(`./node-sh/run.sh ${competitionId} ${userId} ${problemId}`) ? - res.send(JSON.stringify({result: 'SUCCESS', competitionId, userId, problemId})) : - res.send(JSON.stringify({result: 'FAIL', competitionId, userId, problemId})); + // const result = execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId}`); // docker instance용 + // const result = execute(`./node-sh/run.sh ${competitionId} ${userId} ${problemId}`); // local test용 + + const responseJson = { result: '', competitionId, userId, problemId }; + Promise.race([ + execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId}`), + getTimer(TIMEOUT_IN_MILLI), + ]).then(() => { + responseJson.result = 'SUCCESS'; + }).catch((error) => { + responseJson.result = error.message; + }).finally(() => { + res.send(JSON.stringify(responseJson)); + }); }) -app.listen(port, () => { - console.log(`[algo-with-me-docker] listening at port ${port}`); +app.listen(PORT, () => { + console.log(`[algo-with-me-docker] listening at port ${PORT}`); }) From 38160d0e13d20876c45064810cfaeef2458cbc5a Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:33:47 +0900 Subject: [PATCH 026/226] =?UTF-8?q?chore:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 2 + be/algo-with-me-api/pnpm-lock.yaml | 150 +++++++++++++++++++++++++++-- 2 files changed, 145 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index bf43f14..4a2f15d 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -26,7 +26,9 @@ "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/platform-socket.io": "^10.2.8", "@nestjs/typeorm": "^10.0.0", + "@nestjs/websockets": "^10.2.8", "bull": "^4.11.5", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 0047f64..6ab3beb 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -16,16 +16,22 @@ dependencies: version: 3.1.1(@nestjs/common@10.2.8)(reflect-metadata@0.1.13) '@nestjs/core': specifier: ^10.0.0 - version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/mapped-types': specifier: '*' version: 2.0.3(@nestjs/common@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nestjs/platform-socket.io': + specifier: ^10.2.8 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/websockets@10.2.8)(rxjs@7.8.1) '@nestjs/typeorm': specifier: ^10.0.0 version: 10.0.0(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17) + '@nestjs/websockets': + specifier: ^10.2.8 + version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-socket.io@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) bull: specifier: ^4.11.5 version: 4.11.5 @@ -935,7 +941,7 @@ packages: '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) tslib: 2.6.0 dev: false @@ -948,7 +954,7 @@ packages: dependencies: '@nestjs/bull-shared': 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) bull: 4.11.5 tslib: 2.6.0 dev: false @@ -1030,7 +1036,7 @@ packages: uuid: 9.0.0 dev: false - /@nestjs/core@10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1): + /@nestjs/core@10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1): resolution: {integrity: sha512-9+MZ2s8ixfY9Bl/M9ofChiyYymcwdK9ZWNH4GDMF7Am7XRAQ1oqde6MYGG05rhQwiVXuTwaYLlXciJKfsrg5qg==} requiresBuild: true peerDependencies: @@ -1050,6 +1056,7 @@ packages: dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nestjs/websockets': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-socket.io@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -1087,7 +1094,7 @@ packages: '@nestjs/core': ^10.0.0 dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 @@ -1096,6 +1103,23 @@ packages: transitivePeerDependencies: - supports-color + /@nestjs/platform-socket.io@10.2.8(@nestjs/common@10.2.8)(@nestjs/websockets@10.2.8)(rxjs@7.8.1): + resolution: {integrity: sha512-P/Olw9alAaKD7Q1vS/ol7K81x1l7Bmi+AXthBNUPGMmG/W8kxO1krerW4rEhtF3BKJ0qJIa5bhDlb80p4lZcNA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + rxjs: ^7.1.0 + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/websockets': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-socket.io@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + rxjs: 7.8.1 + socket.io: 4.7.2 + tslib: 2.6.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + /@nestjs/schematics@10.0.3(chokidar@3.5.3)(typescript@5.2.2): resolution: {integrity: sha512-2BRujK0GqGQ7j1Zpz+obVfskDnnOeVKt5aXoSaVngKo8Oczy8uYCY+R547TQB+Kf35epdfFER2pVnQrX3/It5A==} peerDependencies: @@ -1125,7 +1149,7 @@ packages: optional: true dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) tslib: 2.6.2 dev: true @@ -1140,13 +1164,34 @@ packages: typeorm: ^0.3.0 dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) reflect-metadata: 0.1.13 rxjs: 7.8.1 typeorm: 0.3.17(pg@8.11.3)(ts-node@10.9.1) uuid: 9.0.0 dev: false + /@nestjs/websockets@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-socket.io@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-oZN1VJFApN7d2eftr65a36QrV0IJNGba4znqyjFnyGvtDWTDcQwzDcnEfvJBTTYhOSBNS7KDfVhne0ythkl6tg==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/platform-socket.io': ^10.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/platform-socket.io': + optional: true + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/platform-socket.io': 10.2.8(@nestjs/common@10.2.8)(@nestjs/websockets@10.2.8)(rxjs@7.8.1) + iterare: 1.2.1 + object-hash: 3.0.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1214,6 +1259,9 @@ packages: '@sinonjs/commons': 3.0.0 dev: true + /@socket.io/component-emitter@3.1.0: + resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + /@sqltools/formatter@1.2.5: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false @@ -1272,10 +1320,18 @@ packages: '@types/node': 20.9.0 dev: true + /@types/cookie@0.4.1: + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + /@types/cookiejar@2.1.4: resolution: {integrity: sha512-b698BLJ6kPVd6uhHsY7wlebZdrWPXYied883PDSzpJZYOP97EOn/oGdLCH3jJf157srkFReIZY5v0H1s8Dozrg==} dev: true + /@types/cors@2.8.16: + resolution: {integrity: sha512-Trx5or1Nyg1Fq138PCuWqoApzvoSLWzZ25ORBiHMbbUT42g578lH1GT4TwYDbiUOLFuDsCkfLneT2105fsFWGg==} + dependencies: + '@types/node': 20.9.0 + /@types/eslint-scope@3.7.7: resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} dependencies: @@ -1983,6 +2039,10 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} @@ -2390,6 +2450,10 @@ packages: /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + /cookie@0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} @@ -2668,6 +2732,29 @@ packages: once: 1.4.0 dev: true + /engine.io-parser@5.2.1: + resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==} + engines: {node: '>=10.0.0'} + + /engine.io@6.5.4: + resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==} + engines: {node: '>=10.2.0'} + dependencies: + '@types/cookie': 0.4.1 + '@types/cors': 2.8.16 + '@types/node': 20.9.0 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.4.2 + cors: 2.8.5 + debug: 4.3.4 + engine.io-parser: 5.2.1 + ws: 8.11.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + /enhanced-resolve@5.15.0: resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} engines: {node: '>=10.13.0'} @@ -4842,6 +4929,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + /object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} @@ -5622,6 +5713,39 @@ packages: engines: {node: '>=8'} dev: true + /socket.io-adapter@2.5.2: + resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==} + dependencies: + ws: 8.11.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + /socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + /socket.io@4.7.2: + resolution: {integrity: sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==} + engines: {node: '>=10.2.0'} + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.4 + engine.io: 6.5.4 + socket.io-adapter: 2.5.2 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -6473,6 +6597,18 @@ packages: signal-exit: 3.0.7 dev: true + /ws@8.11.0: + resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} From a5500c7721a635788b398a8d2685a5c8d89be256 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 17 Nov 2023 01:28:21 +0900 Subject: [PATCH 027/226] =?UTF-8?q?feat:=20=EA=B8=B0=EB=B3=B8=20websocket?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 1 + be/algo-with-me-api/pnpm-lock.yaml | 3 ++ .../src/competition/competition.module.ts | 3 +- .../gateways/competition.gateway.ts | 39 +++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 be/algo-with-me-api/src/competition/gateways/competition.gateway.ts diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index 4a2f15d..e71e20f 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -55,6 +55,7 @@ "eslint-plugin-prettier": "^5.0.0", "jest": "^29.5.0", "prettier": "^3.0.0", + "socket.io": "^4.7.2", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.0", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 6ab3beb..9adf746 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -106,6 +106,9 @@ devDependencies: prettier: specifier: ^3.0.0 version: 3.0.3 + socket.io: + specifier: ^4.7.2 + version: 4.7.2 source-map-support: specifier: ^0.5.21 version: 0.5.21 diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 19a060f..4304686 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -6,6 +6,7 @@ import { CompetitionController } from './controllers/competition.controller'; import { ProblemController } from './controllers/problem.controller'; import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; +import { CompetitionGateWay } from './gateways/competition.gateway'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; import { SubmissionConsumer } from './tem.consumer'; @@ -18,6 +19,6 @@ import { SubmissionConsumer } from './tem.consumer'; }), ], controllers: [ProblemController, CompetitionController], - providers: [ProblemService, CompetitionService, SubmissionConsumer], + providers: [ProblemService, CompetitionService, SubmissionConsumer, CompetitionGateWay], }) export class CompetitionModule {} diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts new file mode 100644 index 0000000..04b180b --- /dev/null +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -0,0 +1,39 @@ +import { + ConnectedSocket, + MessageBody, + OnGatewayConnection, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, + WsResponse, +} from '@nestjs/websockets'; +import { Server, Socket } from 'socket.io'; + +@WebSocketGateway({ namespace: 'competitions' }) +export class CompetitionGateWay implements OnGatewayConnection { + @WebSocketServer() + server: Server; + + @SubscribeMessage('events') + handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { + this.server.emit('events', { data: '데이터 간다' }); + client.emit('events', { data: '데이터 간다22' }); + const event = 'events'; + console.log(client.rooms); + console.log(data); + return { event, data }; + } + + @SubscribeMessage('submissions') + handleSubmission(@MessageBody() data: string, @ConnectedSocket() client: Socket) { + console.log(data, client); + } + + public handleConnection(client: Socket, ...args: any[]) { + // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 + const { competitionId } = client.handshake.query; + client.join(competitionId); + client.leave(client.id); + console.log(competitionId, args); + } +} From 830a5e64d28542c7aada8dd4e1928d78420dac01 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 17 Nov 2023 01:53:14 +0900 Subject: [PATCH 028/226] =?UTF-8?q?fix:=20http=20request=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=ED=95=98=EB=8D=98=EA=B2=83=EC=9D=84=20websoc?= =?UTF-8?q?ket=20=EC=A0=9C=EC=B6=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 13 ++++++------- .../competition/gateways/competition.gateway.ts | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 9fdae57..30d8946 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,6 +1,5 @@ -import { Body, Controller, Get, Param, Post, UsePipes, ValidationPipe } from '@nestjs/common'; +import { Controller, Get, Param } from '@nestjs/common'; -import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { CompetitionService } from '../services/competition.service'; @Controller('competitions') @@ -12,9 +11,9 @@ export class CompetitionController { return this.competitionService.findOneProblem(id); } - @Post('submissions') - @UsePipes(new ValidationPipe({ transform: true })) - createSubmission(@Body() createSubmissionDto: CreateSubmissionDto) { - return this.competitionService.scoreSubmission(createSubmissionDto); - } + // @Post('submissions') + // @UsePipes(new ValidationPipe({ transform: true })) + // createSubmission(@Body() createSubmissionDto: CreateSubmissionDto) { + // return this.competitionService.scoreSubmission(createSubmissionDto); + // } } diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 04b180b..c74d04f 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -9,11 +9,16 @@ import { } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; +import { CreateSubmissionDto } from '../dto/create-submission.dto'; +import { CompetitionService } from '../services/competition.service'; + @WebSocketGateway({ namespace: 'competitions' }) export class CompetitionGateWay implements OnGatewayConnection { @WebSocketServer() server: Server; + constructor(private readonly competitionService: CompetitionService) {} + @SubscribeMessage('events') handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { this.server.emit('events', { data: '데이터 간다' }); @@ -25,8 +30,13 @@ export class CompetitionGateWay implements OnGatewayConnection { } @SubscribeMessage('submissions') - handleSubmission(@MessageBody() data: string, @ConnectedSocket() client: Socket) { - console.log(data, client); + handleSubmission( + @MessageBody() createSubmissionDto: CreateSubmissionDto, + @ConnectedSocket() client: Socket, + ) { + this.competitionService.scoreSubmission(createSubmissionDto); + client.emit('message', { message: '채점을 시작합니다.' }); + console.log(createSubmissionDto, client); } public handleConnection(client: Socket, ...args: any[]) { From c0de525ad0265a5ed0923658be2e48ea36ac545d Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 18 Nov 2023 13:04:44 +0900 Subject: [PATCH 029/226] =?UTF-8?q?fix:=20reids=20queue=EC=97=90=20?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EA=B0=80=EB=8A=94=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=97=90=20socketId=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 5 +++-- .../src/competition/services/competition.service.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index c74d04f..e77f549 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -23,7 +23,9 @@ export class CompetitionGateWay implements OnGatewayConnection { handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { this.server.emit('events', { data: '데이터 간다' }); client.emit('events', { data: '데이터 간다22' }); + this.server.to(client.id).emit('events', { data: '데이터 간다 33' }); const event = 'events'; + console.log(client.id); console.log(client.rooms); console.log(data); return { event, data }; @@ -34,7 +36,7 @@ export class CompetitionGateWay implements OnGatewayConnection { @MessageBody() createSubmissionDto: CreateSubmissionDto, @ConnectedSocket() client: Socket, ) { - this.competitionService.scoreSubmission(createSubmissionDto); + this.competitionService.scoreSubmission(createSubmissionDto, client.id); client.emit('message', { message: '채점을 시작합니다.' }); console.log(createSubmissionDto, client); } @@ -43,7 +45,6 @@ export class CompetitionGateWay implements OnGatewayConnection { // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 const { competitionId } = client.handshake.query; client.join(competitionId); - client.leave(client.id); console.log(competitionId, args); } } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index f156a5f..e35d621 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -37,7 +37,7 @@ export class CompetitionService { }; } - async scoreSubmission(createSubmissionDto: CreateSubmissionDto) { + async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string) { const problem: Problem = await this.problemRepository.findOneBy({ id: createSubmissionDto.problemId, }); @@ -46,6 +46,7 @@ export class CompetitionService { await this.submissionQueue.add({ problemId: savedSubmission.problem.id, submissionId: savedSubmission.id, + socketId: socketId, }); return savedSubmission; From c577f4d8ba6ee4cdc33f0d3306f937bde1e8c62b Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:02:09 +0900 Subject: [PATCH 030/226] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B1=84=EC=A0=90=EC=99=84?= =?UTF-8?q?=EB=A3=8C=EC=8B=9C=20=EC=A0=80=EC=9E=A5&=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=EC=97=90=EA=B2=8C=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=ED=95=98=EB=8A=94=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/dto/score-result.dto.ts | 22 ++++++++++++++++ .../competition/entities/submission.entity.ts | 4 +-- .../services/competition.service.ts | 25 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/dto/score-result.dto.ts diff --git a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts new file mode 100644 index 0000000..f41265f --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts @@ -0,0 +1,22 @@ +import { IsEnum, IsNotEmpty } from 'class-validator'; + +import { RESULT } from '../competition.enums'; + +export class ScoreResultDto { + @IsNotEmpty() + submissionId: number; + + @IsNotEmpty() + problemId: number; + + @IsNotEmpty() + testcaseId: number; + + @IsNotEmpty() + socketId: string; + + @IsEnum(RESULT) + result: string; + + stdOut: string; +} diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts index d9e97da..5f2596d 100644 --- a/be/algo-with-me-api/src/competition/entities/submission.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -25,8 +25,8 @@ export class Submission { }) result: string; - @Column('json', { nullable: true }) - detail: string; + @Column('json', { nullable: true, default: [] }) + detail: object[]; @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) problem: Problem; diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index e35d621..3fc5850 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,18 +1,24 @@ import { InjectQueue } from '@nestjs/bull'; import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { WebSocketServer } from '@nestjs/websockets'; import { Queue } from 'bull'; +import { Server } from 'socket.io'; import { Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; +import { ScoreResultDto } from '../dto/score-result.dto'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; @Injectable() export class CompetitionService { + @WebSocketServer() + server: Server; + constructor( @InjectRepository(Problem) private readonly problemRepository: Repository , @InjectRepository(Submission) private readonly submissionRepository: Repository , @@ -51,4 +57,23 @@ export class CompetitionService { return savedSubmission; } + + async saveScoreResult(scoreResultDto: ScoreResultDto) { + const submission = await this.submissionRepository.findOneBy({ + id: scoreResultDto.submissionId, + }); + if (!submission) throw new NotFoundException('제출 기록이 없습니다.'); + + const result = { + testcaseId: scoreResultDto.testcaseId, + result: scoreResultDto.result, + stdOut: scoreResultDto.stdOut, + }; + + submission.detail.push(result); + this.submissionRepository.save(submission); + + result['problemId'] = scoreResultDto.problemId; + this.server.to(scoreResultDto.socketId).emit("message", result); + } } From d5a30b19ba9334a264d95eaa56a8e78c9ea70e49 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:55:23 +0900 Subject: [PATCH 031/226] =?UTF-8?q?feat:=20=EC=B1=84=EC=A0=90=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20api=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84,=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 9 ++++++++- .../gateways/competition.gateway.ts | 18 ++++++++++++++---- .../services/competition.service.ts | 7 ++----- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 30d8946..d917264 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,5 +1,6 @@ -import { Controller, Get, Param } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, UsePipes, ValidationPipe } from '@nestjs/common'; +import { ScoreResultDto } from '../dto/score-result.dto'; import { CompetitionService } from '../services/competition.service'; @Controller('competitions') @@ -11,6 +12,12 @@ export class CompetitionController { return this.competitionService.findOneProblem(id); } + @Post('scores') + @UsePipes(new ValidationPipe({ transform: true })) + saveScoreResult(@Body() scoreResultDto: ScoreResultDto) { + this.competitionService.saveScoreResult(scoreResultDto); + } + // @Post('submissions') // @UsePipes(new ValidationPipe({ transform: true })) // createSubmission(@Body() createSubmissionDto: CreateSubmissionDto) { diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index e77f549..ef0ab6d 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,7 +1,9 @@ +import { UsePipes, ValidationPipe } from '@nestjs/common'; import { ConnectedSocket, MessageBody, OnGatewayConnection, + OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketServer, @@ -13,12 +15,16 @@ import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { CompetitionService } from '../services/competition.service'; @WebSocketGateway({ namespace: 'competitions' }) -export class CompetitionGateWay implements OnGatewayConnection { +export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @WebSocketServer() server: Server; constructor(private readonly competitionService: CompetitionService) {} + afterInit(server: Server) { + this.competitionService.server = server; + } + @SubscribeMessage('events') handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { this.server.emit('events', { data: '데이터 간다' }); @@ -32,19 +38,23 @@ export class CompetitionGateWay implements OnGatewayConnection { } @SubscribeMessage('submissions') + @UsePipes(new ValidationPipe({ transform: true })) handleSubmission( @MessageBody() createSubmissionDto: CreateSubmissionDto, @ConnectedSocket() client: Socket, - ) { + ): WsResponse { this.competitionService.scoreSubmission(createSubmissionDto, client.id); - client.emit('message', { message: '채점을 시작합니다.' }); - console.log(createSubmissionDto, client); + console.log(createSubmissionDto); + const event = 'messages'; + const data = { message: '채점을 시작합니다.' }; + return { event, data }; } public handleConnection(client: Socket, ...args: any[]) { // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 const { competitionId } = client.handshake.query; client.join(competitionId); + console.log(client.id); console.log(competitionId, args); } } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 3fc5850..e9023e7 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,7 +1,6 @@ import { InjectQueue } from '@nestjs/bull'; import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { WebSocketServer } from '@nestjs/websockets'; import { Queue } from 'bull'; import { Server } from 'socket.io'; import { Repository } from 'typeorm'; @@ -16,9 +15,7 @@ import { Submission } from '../entities/submission.entity'; @Injectable() export class CompetitionService { - @WebSocketServer() server: Server; - constructor( @InjectRepository(Problem) private readonly problemRepository: Repository , @InjectRepository(Submission) private readonly submissionRepository: Repository , @@ -67,13 +64,13 @@ export class CompetitionService { const result = { testcaseId: scoreResultDto.testcaseId, result: scoreResultDto.result, - stdOut: scoreResultDto.stdOut, }; submission.detail.push(result); this.submissionRepository.save(submission); result['problemId'] = scoreResultDto.problemId; - this.server.to(scoreResultDto.socketId).emit("message", result); + result['stdOut'] = scoreResultDto.stdOut; + this.server.to(scoreResultDto.socketId).emit('messages', result); } } From b3a37785152fa924d72b97a3b7d87e611ff90dd8 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:10:36 +0900 Subject: [PATCH 032/226] =?UTF-8?q?fix:=20websocket=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=B2=98=EB=A6=AC,=20=EC=9D=B4=EB=B2=A4=EB=93=9C=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/competition.enums.ts | 2 +- .../gateways/competition.gateway.ts | 24 +++++++++---------- .../services/competition.service.ts | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/be/algo-with-me-api/src/competition/competition.enums.ts b/be/algo-with-me-api/src/competition/competition.enums.ts index 03836b2..6c75029 100644 --- a/be/algo-with-me-api/src/competition/competition.enums.ts +++ b/be/algo-with-me-api/src/competition/competition.enums.ts @@ -1,7 +1,7 @@ export const RESULT = { PROGRESS: '처리중', CORRECT: '정답입니다', - WRONG: '오답입니다.', + WRONG: '오답입니다', TIMEOUT: '시간초과', OOM: '메모리초과', } as const; diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index ef0ab6d..c322c82 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -25,17 +25,17 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { this.competitionService.server = server; } - @SubscribeMessage('events') - handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { - this.server.emit('events', { data: '데이터 간다' }); - client.emit('events', { data: '데이터 간다22' }); - this.server.to(client.id).emit('events', { data: '데이터 간다 33' }); - const event = 'events'; - console.log(client.id); - console.log(client.rooms); - console.log(data); - return { event, data }; - } + // @SubscribeMessage('events') + // handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { + // this.server.emit('events', { data: '데이터 간다' }); + // client.emit('events', { data: '데이터 간다22' }); + // this.server.to(client.id).emit('events', { data: '데이터 간다 33' }); + // const event = 'events'; + // console.log(client.id); + // console.log(client.rooms); + // console.log(data); + // return { event, data }; + // } @SubscribeMessage('submissions') @UsePipes(new ValidationPipe({ transform: true })) @@ -44,9 +44,9 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @ConnectedSocket() client: Socket, ): WsResponse { this.competitionService.scoreSubmission(createSubmissionDto, client.id); - console.log(createSubmissionDto); const event = 'messages'; const data = { message: '채점을 시작합니다.' }; + console.log(createSubmissionDto); return { event, data }; } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index e9023e7..d7dab1e 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -71,6 +71,6 @@ export class CompetitionService { result['problemId'] = scoreResultDto.problemId; result['stdOut'] = scoreResultDto.stdOut; - this.server.to(scoreResultDto.socketId).emit('messages', result); + this.server.to(scoreResultDto.socketId).emit('scoreResult', result); } } From cef68d86638aab2a126bcc75de760676667cc0c9 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:12:08 +0900 Subject: [PATCH 033/226] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/services/competition.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index d7dab1e..e5c4e7e 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -55,6 +55,7 @@ export class CompetitionService { return savedSubmission; } + // TODO: 유저, 대회 도메인 구현 이후 수정 필요 async saveScoreResult(scoreResultDto: ScoreResultDto) { const submission = await this.submissionRepository.findOneBy({ id: scoreResultDto.submissionId, From eab02decf6e9923cd6ab08f23b297f240884393a Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:33:44 +0900 Subject: [PATCH 034/226] =?UTF-8?q?chore:=20todo=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index c322c82..0229cab 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -38,6 +38,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { // } @SubscribeMessage('submissions') + // TODO: 검증 실패시 에러 터져버리고, websocket으로 internal server error 가는거 수정해야됨. @UsePipes(new ValidationPipe({ transform: true })) handleSubmission( @MessageBody() createSubmissionDto: CreateSubmissionDto, From a0e9bf544f41426ba8e8b91c61831f26b9047c4b Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Sun, 19 Nov 2023 15:02:25 +0900 Subject: [PATCH 035/226] =?UTF-8?q?feat:=20swagger=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 1 + be/algo-with-me-api/pnpm-lock.yaml | 38 ++++++++++++++++++++++++++++-- be/algo-with-me-api/src/main.ts | 13 ++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index e71e20f..3ccbac6 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -27,6 +27,7 @@ "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^10.2.8", + "@nestjs/swagger": "^7.1.16", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.8", "bull": "^4.11.5", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 9adf746..2511ca2 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -26,6 +26,9 @@ dependencies: '@nestjs/platform-socket.io': specifier: ^10.2.8 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/websockets@10.2.8)(rxjs@7.8.1) + '@nestjs/swagger': + specifier: ^7.1.16 + version: 7.1.16(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) '@nestjs/typeorm': specifier: ^10.0.0 version: 10.0.0(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17) @@ -1138,6 +1141,35 @@ packages: - chokidar dev: true + /@nestjs/swagger@7.1.16(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): + resolution: {integrity: sha512-f9KBk/BX9MUKPTj7tQNYJ124wV/jP5W2lwWHLGwe/4qQXixuDOo39zP55HIJ44LE7S04B7BOeUOo9GBJD/vRcw==} + peerDependencies: + '@fastify/static': ^6.0.0 + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 + peerDependenciesMeta: + '@fastify/static': + optional: true + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.3(@nestjs/common@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) + class-transformer: 0.5.1 + class-validator: 0.14.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + path-to-regexp: 3.2.0 + reflect-metadata: 0.1.13 + swagger-ui-dist: 5.9.1 + dev: false + /@nestjs/testing@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8): resolution: {integrity: sha512-9Kj5IQhM67/nj/MT6Wi2OmWr5YQnCMptwKVFrX1TDaikpY12196v7frk0jVjdT7wms7rV07GZle9I2z0aSjqtQ==} peerDependencies: @@ -1875,7 +1907,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -4506,7 +4537,6 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - dev: true /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} @@ -5953,6 +5983,10 @@ packages: engines: {node: '>= 0.4'} dev: true + /swagger-ui-dist@5.9.1: + resolution: {integrity: sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==} + dev: false + /symbol-observable@4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} engines: {node: '>=0.10'} diff --git a/be/algo-with-me-api/src/main.ts b/be/algo-with-me-api/src/main.ts index d5cd71b..8bc1d10 100644 --- a/be/algo-with-me-api/src/main.ts +++ b/be/algo-with-me-api/src/main.ts @@ -1,9 +1,22 @@ import { NestFactory } from '@nestjs/core'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from '@src/app.module'; +function setSwagger(app) { + const config = new DocumentBuilder() + .setTitle('algo-with-me-api') + .setDescription('algo with me API description') + .setVersion('1.0') + .build(); + + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); +} + async function bootstrap() { const app = await NestFactory.create(AppModule); + setSwagger(app); await app.listen(3000); } bootstrap(); From 3fb445fc8515d1ce37c4dbe29816efc9a93a42e5 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:24:36 +0900 Subject: [PATCH 036/226] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=20controller?= =?UTF-8?q?=EC=97=90=20swagger=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 2 + .../controllers/problem.controller.ts | 12 ++++++ .../src/competition/dto/create-problem.dto.ts | 7 ++++ .../dto/problem.list.response.dto.ts | 14 +++++++ .../competition/dto/problem.response.dto.ts | 37 +++++++++++++++++++ .../competition/entities/problem.entity.ts | 11 ++++++ .../competition/services/problem.service.ts | 23 ++++++------ 7 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts create mode 100644 be/algo-with-me-api/src/competition/dto/problem.response.dto.ts diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index d917264..a4fecff 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,8 +1,10 @@ import { Body, Controller, Get, Param, Post, UsePipes, ValidationPipe } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; import { ScoreResultDto } from '../dto/score-result.dto'; import { CompetitionService } from '../services/competition.service'; +@ApiTags('competitions') @Controller('competitions') export class CompetitionController { constructor(private readonly competitionService: CompetitionService) {} diff --git a/be/algo-with-me-api/src/competition/controllers/problem.controller.ts b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts index 0d7a267..217f58b 100644 --- a/be/algo-with-me-api/src/competition/controllers/problem.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts @@ -8,26 +8,37 @@ import { Param, Delete, } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CreateProblemDto } from '../dto/create-problem.dto'; +import { ProblemListResponseDto } from '../dto/problem.list.response.dto'; +import { ProblemResponseDto } from '../dto/problem.response.dto'; +import { Problem } from '../entities/problem.entity'; import { ProblemService } from '../services/problem.service'; +@ApiTags('문제(problems)') @Controller('problems') export class ProblemController { constructor(private readonly problemService: ProblemService) {} @Post() + @ApiOperation({ summary: '문제 생성', description: '문제를 생성한다.' }) + @ApiCreatedResponse({ type: Problem }) @UsePipes(new ValidationPipe({ transform: true })) create(@Body() createProblemDto: CreateProblemDto) { return this.problemService.create(createProblemDto); } @Get() + @ApiOperation({ summary: '문제 전체 조회', description: '문제 목록을 조회한다.' }) + @ApiResponse({ type: ProblemListResponseDto, isArray: true }) findAll() { return this.problemService.findAll(); } @Get(':id') + @ApiOperation({ summary: '문제 세부 정보 조회', description: '문제 세부 정보를 조회한다.' }) + @ApiResponse({ type: ProblemResponseDto }) findOne(@Param('id') id: number) { return this.problemService.findOne(id); } @@ -38,6 +49,7 @@ export class ProblemController { // } @Delete(':id') + @ApiOperation({ summary: '문제 삭제' }) remove(@Param('id') id: number) { this.problemService.remove(id); } diff --git a/be/algo-with-me-api/src/competition/dto/create-problem.dto.ts b/be/algo-with-me-api/src/competition/dto/create-problem.dto.ts index 76f2f8f..18f410c 100644 --- a/be/algo-with-me-api/src/competition/dto/create-problem.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-problem.dto.ts @@ -1,23 +1,30 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; import { Problem } from '../entities/problem.entity'; export class CreateProblemDto { + @ApiProperty() @IsNotEmpty() title: string; + @ApiProperty() @IsNotEmpty() timeLimit: number; + @ApiProperty() @IsNotEmpty() memoryLimit: number; + @ApiProperty() @IsNotEmpty() testcaseNum: number; + @ApiProperty() @IsNotEmpty() frameCode: string; + @ApiProperty() @IsNotEmpty() solutionCode: string; diff --git a/be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts b/be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts new file mode 100644 index 0000000..8698796 --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ProblemListResponseDto { + constructor(id: number, title: string) { + this.id = id; + this.title = title; + } + + @ApiProperty() + id: number; + + @ApiProperty() + title: string; +} diff --git a/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts b/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts new file mode 100644 index 0000000..76d75fa --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ProblemResponseDto { + constructor( + id: number, + title: string, + timeLimit: number, + memoryLimit: number, + content: string, + createdAt: Date, + ) { + this.id = id; + this.title = title; + this.timeLimit = timeLimit; + this.memoryLimit = memoryLimit; + this.content = content; + this.createdAt = createdAt; + } + + @ApiProperty() + id: number; + + @ApiProperty() + title: string; + + @ApiProperty({ description: '시간제한(ms)' }) + timeLimit: number; + + @ApiProperty({ description: '메모리제한(KB)' }) + memoryLimit: number; + + @ApiProperty({ description: '문제 내용' }) + content: string; + + @ApiProperty() + createdAt: Date; +} diff --git a/be/algo-with-me-api/src/competition/entities/problem.entity.ts b/be/algo-with-me-api/src/competition/entities/problem.entity.ts index 1783663..ebbcc93 100644 --- a/be/algo-with-me-api/src/competition/entities/problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/problem.entity.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from '@nestjs/swagger'; import { Column, CreateDateColumn, @@ -12,32 +13,42 @@ import { Submission } from './submission.entity'; @Entity() export class Problem { @PrimaryGeneratedColumn() + @ApiProperty() id: number; + @ApiProperty() @Column() title: string; + @ApiProperty() @Column() timeLimit: number; + @ApiProperty() @Column() memoryLimit: number; + @ApiProperty() @Column() testcaseNum: number; + @ApiProperty() @Column('text') frameCode: string; + @ApiProperty() @Column('text') solutionCode: string; + @ApiProperty() @OneToMany(() => Submission, (submission) => submission.problem) submissions: Submission[]; + @ApiProperty() @CreateDateColumn() createdAt: Date; + @ApiProperty() @UpdateDateColumn() updatedAt: Date; } diff --git a/be/algo-with-me-api/src/competition/services/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts index ee50ecb..30765a7 100644 --- a/be/algo-with-me-api/src/competition/services/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -6,6 +6,8 @@ import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; import { CreateProblemDto } from '../dto/create-problem.dto'; +import { ProblemListResponseDto } from '../dto/problem.list.response.dto'; +import { ProblemResponseDto } from '../dto/problem.response.dto'; import { Problem } from '../entities/problem.entity'; @Injectable() @@ -22,10 +24,7 @@ export class ProblemService { async findAll() { const problems = await this.problemRepository.find(); return problems.map((problem: Problem) => { - return { - id: problem.id, - title: problem.title, - }; + return new ProblemListResponseDto(problem.id, problem.title); }); } @@ -35,14 +34,14 @@ export class ProblemService { const paths = path.join(process.env.PROBLEM_PATH, id.toString(), fileName); if (!existsSync(paths)) throw new NotFoundException('문제 파일을 찾을 수 없습니다.'); const content = readFileSync(paths).toString(); - return { - id: problem.id, - title: problem.title, - timeLimit: problem.timeLimit, - memoryLimit: problem.memoryLimit, - content: content, - createdAt: problem.createdAt, - }; + return new ProblemResponseDto( + problem.id, + problem.title, + problem.timeLimit, + problem.memoryLimit, + content, + problem.createdAt, + ); } // update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From 0adc7ab7e0271984cce8edbdb181898aa07bbce7 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:08:57 +0900 Subject: [PATCH 037/226] =?UTF-8?q?=EB=8C=80=ED=9A=8C=20controller?= =?UTF-8?q?=EC=97=90=20swagger=20=EC=A0=81=EC=9A=A9:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 11 ++++- .../controllers/problem.controller.ts | 2 +- .../dto/competition.problem.response.dto.ts | 47 +++++++++++++++++++ .../src/competition/dto/score-result.dto.ts | 7 +++ .../services/competition.service.ts | 21 +++++---- 5 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/dto/competition.problem.response.dto.ts diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index a4fecff..a84c82d 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,20 +1,27 @@ import { Body, Controller, Get, Param, Post, UsePipes, ValidationPipe } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; import { CompetitionService } from '../services/competition.service'; -@ApiTags('competitions') +@ApiTags('대회(competitions)') @Controller('competitions') export class CompetitionController { constructor(private readonly competitionService: CompetitionService) {} @Get('problems/:id') + @ApiOperation({ summary: '대회 문제 상세 조회' }) + @ApiResponse({ type: CompetitionProblemResponseDto }) findOne(@Param('id') id: number) { return this.competitionService.findOneProblem(id); } @Post('scores') + @ApiOperation({ + summary: '채점 서버에서 채점 완료시 요청받을 api', + description: '채점 서버에서 테스트케이스 별로 채점이 완료될경우 호출할 api', + }) @UsePipes(new ValidationPipe({ transform: true })) saveScoreResult(@Body() scoreResultDto: ScoreResultDto) { this.competitionService.saveScoreResult(scoreResultDto); diff --git a/be/algo-with-me-api/src/competition/controllers/problem.controller.ts b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts index 217f58b..d73c533 100644 --- a/be/algo-with-me-api/src/competition/controllers/problem.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts @@ -37,7 +37,7 @@ export class ProblemController { } @Get(':id') - @ApiOperation({ summary: '문제 세부 정보 조회', description: '문제 세부 정보를 조회한다.' }) + @ApiOperation({ summary: '문제 상세 조회', description: '문제 세부 정보를 조회한다.' }) @ApiResponse({ type: ProblemResponseDto }) findOne(@Param('id') id: number) { return this.problemService.findOne(id); diff --git a/be/algo-with-me-api/src/competition/dto/competition.problem.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.problem.response.dto.ts new file mode 100644 index 0000000..8699f03 --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/competition.problem.response.dto.ts @@ -0,0 +1,47 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CompetitionProblemResponseDto { + constructor( + id: number, + title: string, + timeLimit: number, + memoryLimit: number, + content: string, + solutionCode: string, + testcases: object[], + createdAt: Date, + ) { + this.id = id; + this.title = title; + this.timeLimit = timeLimit; + this.memoryLimit = memoryLimit; + this.content = content; + this.solutionCode = solutionCode; + this.testcases = testcases; + this.createdAt = createdAt; + } + + @ApiProperty() + id: number; + + @ApiProperty() + title: string; + + @ApiProperty({ description: '시간제한(ms)' }) + timeLimit: number; + + @ApiProperty({ description: '메모리제한(KB)' }) + memoryLimit: number; + + @ApiProperty({ description: '문제 내용' }) + content: string; + + @ApiProperty({ description: '초기 코드' }) + solutionCode: string; + + @ApiProperty({ description: '공개 테스트 케이스' }) + testcases: object[]; + + @ApiProperty() + createdAt: Date; +} diff --git a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts index f41265f..a7222f8 100644 --- a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts @@ -1,22 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsNotEmpty } from 'class-validator'; import { RESULT } from '../competition.enums'; export class ScoreResultDto { + @ApiProperty() @IsNotEmpty() submissionId: number; + @ApiProperty() @IsNotEmpty() problemId: number; + @ApiProperty() @IsNotEmpty() testcaseId: number; + @ApiProperty() @IsNotEmpty() socketId: string; + @ApiProperty() @IsEnum(RESULT) result: string; + @ApiProperty() stdOut: string; } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index e5c4e7e..82a41c6 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -8,6 +8,7 @@ import { Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; +import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; import { Problem } from '../entities/problem.entity'; @@ -28,16 +29,16 @@ export class CompetitionService { const paths = path.join(process.env.PROBLEM_PATH, id.toString(), fileName); if (!existsSync(paths)) throw new NotFoundException('문제 파일을 찾을 수 없습니다.'); const content = readFileSync(paths).toString(); - return { - id: problem.id, - title: problem.title, - timeLimit: problem.timeLimit, - memoryLimit: problem.memoryLimit, - content: content, - solutionCode: problem.solutionCode, - testcases: '임시', - createdAt: problem.createdAt, - }; + return new CompetitionProblemResponseDto( + problem.id, + problem.title, + problem.timeLimit, + problem.memoryLimit, + content, + problem.solutionCode, + [{ temp: '임시' }], + problem.createdAt, + ); } async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string) { From f704f8f60f3890f2e2e0ba0efbcabaeefb33febf Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:37:53 +0900 Subject: [PATCH 038/226] =?UTF-8?q?chore:=20=ED=83=80=EC=9E=85=20=ED=9E=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/main.ts b/be/algo-with-me-api/src/main.ts index 8bc1d10..b39f136 100644 --- a/be/algo-with-me-api/src/main.ts +++ b/be/algo-with-me-api/src/main.ts @@ -1,9 +1,10 @@ +import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from '@src/app.module'; -function setSwagger(app) { +function setSwagger(app: INestApplication ) { const config = new DocumentBuilder() .setTitle('algo-with-me-api') .setDescription('algo with me API description') From debec3da63f6a86bc3a5e632dbd77846d9ff405f Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:01:56 +0900 Subject: [PATCH 039/226] =?UTF-8?q?chore:=20swagger=20package=EB=A5=BC=20d?= =?UTF-8?q?evDependency=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 2 +- be/algo-with-me-api/pnpm-lock.yaml | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index 3ccbac6..fa6c14f 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -27,7 +27,6 @@ "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^10.2.8", - "@nestjs/swagger": "^7.1.16", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.8", "bull": "^4.11.5", @@ -41,6 +40,7 @@ "devDependencies": { "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", + "@nestjs/swagger": "^7.1.16", "@nestjs/testing": "^10.0.0", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 2511ca2..f872a1b 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -26,9 +26,6 @@ dependencies: '@nestjs/platform-socket.io': specifier: ^10.2.8 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/websockets@10.2.8)(rxjs@7.8.1) - '@nestjs/swagger': - specifier: ^7.1.16 - version: 7.1.16(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) '@nestjs/typeorm': specifier: ^10.0.0 version: 10.0.0(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17) @@ -64,6 +61,9 @@ devDependencies: '@nestjs/schematics': specifier: ^10.0.0 version: 10.0.3(chokidar@3.5.3)(typescript@5.2.2) + '@nestjs/swagger': + specifier: ^7.1.16 + version: 7.1.16(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) '@nestjs/testing': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8) @@ -1091,7 +1091,6 @@ packages: class-transformer: 0.5.1 class-validator: 0.14.0 reflect-metadata: 0.1.13 - dev: false /@nestjs/platform-express@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): resolution: {integrity: sha512-WoSSVtwIRc5AdGMHWVzWZK4JZLT0f4o2xW8P9gQvcX+omL8W1kXCfY8GQYXNBG84XmBNYH8r0FtC8oMe/lH5NQ==} @@ -1168,7 +1167,7 @@ packages: path-to-regexp: 3.2.0 reflect-metadata: 0.1.13 swagger-ui-dist: 5.9.1 - dev: false + dev: true /@nestjs/testing@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(@nestjs/platform-express@10.2.8): resolution: {integrity: sha512-9Kj5IQhM67/nj/MT6Wi2OmWr5YQnCMptwKVFrX1TDaikpY12196v7frk0jVjdT7wms7rV07GZle9I2z0aSjqtQ==} @@ -1907,6 +1906,7 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -4537,6 +4537,7 @@ packages: hasBin: true dependencies: argparse: 2.0.1 + dev: true /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} @@ -5985,7 +5986,7 @@ packages: /swagger-ui-dist@5.9.1: resolution: {integrity: sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==} - dev: false + dev: true /symbol-observable@4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} From 1eac9dd71a026d0726d8bd98f549ab98d48183e9 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 09:20:16 +0900 Subject: [PATCH 040/226] =?UTF-8?q?chore:=20gitignore=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/.gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-score/.gitignore b/be/algo-with-me-score/.gitignore index 22f55ad..ea48d65 100644 --- a/be/algo-with-me-score/.gitignore +++ b/be/algo-with-me-score/.gitignore @@ -32,4 +32,6 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +.env \ No newline at end of file From 49a6bf68023afaefc4134b052e9eda148d53e7bf Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 18:28:30 +0900 Subject: [PATCH 041/226] =?UTF-8?q?chore:=20nestjs,=20bull=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/package.json | 2 + be/algo-with-me-score/pnpm-lock.yaml | 210 +++++++++++++++++- .../src/app.controller.spec.ts | 23 -- be/algo-with-me-score/src/app.controller.ts | 13 -- be/algo-with-me-score/src/app.module.ts | 9 +- be/algo-with-me-score/src/app.service.ts | 8 - .../src/score/score.controller.spec.ts | 18 ++ .../src/score/score.controller.ts | 4 + .../src/score/score.module.ts | 4 + .../src/score/score.service.spec.ts | 18 ++ .../src/score/score.service.ts | 4 + 11 files changed, 260 insertions(+), 53 deletions(-) delete mode 100644 be/algo-with-me-score/src/app.controller.spec.ts delete mode 100644 be/algo-with-me-score/src/app.controller.ts delete mode 100644 be/algo-with-me-score/src/app.service.ts create mode 100644 be/algo-with-me-score/src/score/score.controller.spec.ts create mode 100644 be/algo-with-me-score/src/score/score.controller.ts create mode 100644 be/algo-with-me-score/src/score/score.module.ts create mode 100644 be/algo-with-me-score/src/score/score.service.spec.ts create mode 100644 be/algo-with-me-score/src/score/score.service.ts diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index 448763c..cccbfa3 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -20,9 +20,11 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@nestjs/bull": "^10.0.1", "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "bull": "^4.11.5", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index 92a60ff..f70db4d 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@nestjs/bull': + specifier: ^10.0.1 + version: 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(bull@4.11.5) '@nestjs/common': specifier: ^10.0.0 version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) @@ -14,6 +17,9 @@ dependencies: '@nestjs/platform-express': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + bull: + specifier: ^4.11.5 + version: 4.11.5 reflect-metadata: specifier: ^0.1.13 version: 0.1.13 @@ -556,6 +562,10 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true + /@ioredis/commands@1.2.0: + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + dev: false + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -846,6 +856,79 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: + resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2: + resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2: + resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2: + resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2: + resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2: + resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@nestjs/bull-shared@10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): + resolution: {integrity: sha512-8Td36l2i5x9+iQWjPB5Bd5+6u5Eangb5DclNcwrdwKqvd28xE92MSW97P4JV52C2kxrTjZwx8ck/wObAwtpQPw==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + tslib: 2.6.0 + dev: false + + /@nestjs/bull@10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(bull@4.11.5): + resolution: {integrity: sha512-1GcJ8BkHDgQdBMZ7SqAqgUHiFnISXmpGvewFeTc8wf87JLk2PweiKv9j9/KQKU+NI237pCe82XB0bXzTnsdxSw==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + bull: ^3.3 || ^4.0.0 + dependencies: + '@nestjs/bull-shared': 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + bull: 4.11.5 + tslib: 2.6.0 + dev: false + /@nestjs/cli@10.2.1: resolution: {integrity: sha512-CAJAQwmxFZfB3RTvqz/eaXXWpyU+mZ4QSqfBYzjneTsPgF+uyOAW3yQpaLNn9Dfcv39R9UxSuAhayv6yuFd+Jg==} engines: {node: '>= 16.14'} @@ -1936,6 +2019,21 @@ packages: ieee754: 1.2.1 dev: true + /bull@4.11.5: + resolution: {integrity: sha512-9jazyvBBYr55IRDkfJh/mJjWiq8NJUMoCC5zTuBX4JhkZvVXegnwsaIa1jr3x9xwSxGvWEhwQ9lt1jlCT5j6pQ==} + engines: {node: '>=12'} + dependencies: + cron-parser: 4.9.0 + get-port: 5.1.1 + ioredis: 5.3.2 + lodash: 4.17.21 + msgpackr: 1.9.9 + semver: 7.5.4 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + dev: false + /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -2073,6 +2171,11 @@ packages: engines: {node: '>=0.8'} dev: true + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -2222,6 +2325,13 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} + dependencies: + luxon: 3.4.4 + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2262,7 +2372,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /dedent@1.5.1: resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} @@ -2333,6 +2442,11 @@ packages: engines: {node: '>=0.4.0'} dev: true + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: false + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3126,6 +3240,11 @@ packages: engines: {node: '>=8.0.0'} dev: true + /get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + dev: false + /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -3428,6 +3547,23 @@ packages: engines: {node: '>= 0.10'} dev: true + /ioredis@5.3.2: + resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} + engines: {node: '>=12.22.0'} + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.4 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4235,6 +4371,14 @@ packages: p-locate: 5.0.0 dev: true + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: false + + /lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + dev: false + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -4245,7 +4389,6 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -4273,7 +4416,11 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true + + /luxon@3.4.4: + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} + dev: false /macos-release@2.5.1: resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} @@ -4414,11 +4561,32 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + /msgpackr-extract@3.0.2: + resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} + hasBin: true + requiresBuild: true + dependencies: + node-gyp-build-optional-packages: 5.0.7 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2 + dev: false + optional: true + + /msgpackr@1.9.9: + resolution: {integrity: sha512-sbn6mioS2w0lq1O6PpGtsv6Gy8roWM+o3o4Sqjd6DudrL/nOugY+KyJUimoWzHnf9OkO0T6broHFnYE/R05t9A==} + optionalDependencies: + msgpackr-extract: 3.0.2 + dev: false + /multer@1.4.4-lts.1: resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} engines: {node: '>= 6.0.0'} @@ -4468,6 +4636,13 @@ packages: dependencies: whatwg-url: 5.0.0 + /node-gyp-build-optional-packages@5.0.7: + resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} + hasBin: true + requiresBuild: true + dev: false + optional: true + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -4887,6 +5062,18 @@ packages: resolve: 1.22.8 dev: true + /redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + dev: false + + /redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: false + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} @@ -5047,7 +5234,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -5189,6 +5375,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: false + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -5582,6 +5772,10 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true + /tslib@2.6.0: + resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + dev: false + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -5718,6 +5912,11 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true @@ -5904,7 +6103,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} diff --git a/be/algo-with-me-score/src/app.controller.spec.ts b/be/algo-with-me-score/src/app.controller.spec.ts deleted file mode 100644 index ccea57f..0000000 --- a/be/algo-with-me-score/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/be/algo-with-me-score/src/app.controller.ts b/be/algo-with-me-score/src/app.controller.ts deleted file mode 100644 index 0a8e973..0000000 --- a/be/algo-with-me-score/src/app.controller.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; - -import { AppService } from '@src/app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 08e5c5f..3598986 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -2,10 +2,13 @@ import { Module } from '@nestjs/common'; import { AppController } from '@src/app.controller'; import { AppService } from '@src/app.service'; +import { ScoreController } from './score/score.controller'; +import { ScoreService } from './score/score.service'; +import { ScoreModule } from './score/score.module'; @Module({ - imports: [], - controllers: [AppController], - providers: [AppService], + imports: [ScoreModule], + controllers: [AppController, ScoreController], + providers: [AppService, ScoreService], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts deleted file mode 100644 index 927d7cc..0000000 --- a/be/algo-with-me-score/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/be/algo-with-me-score/src/score/score.controller.spec.ts b/be/algo-with-me-score/src/score/score.controller.spec.ts new file mode 100644 index 0000000..6ad431e --- /dev/null +++ b/be/algo-with-me-score/src/score/score.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ScoreController } from './score.controller'; + +describe('ScoreController', () => { + let controller: ScoreController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ScoreController], + }).compile(); + + controller = module.get (ScoreController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/be/algo-with-me-score/src/score/score.controller.ts b/be/algo-with-me-score/src/score/score.controller.ts new file mode 100644 index 0000000..5d4b454 --- /dev/null +++ b/be/algo-with-me-score/src/score/score.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller('score') +export class ScoreController {} diff --git a/be/algo-with-me-score/src/score/score.module.ts b/be/algo-with-me-score/src/score/score.module.ts new file mode 100644 index 0000000..082e101 --- /dev/null +++ b/be/algo-with-me-score/src/score/score.module.ts @@ -0,0 +1,4 @@ +import { Module } from '@nestjs/common'; + +@Module({}) +export class ScoreModule {} diff --git a/be/algo-with-me-score/src/score/score.service.spec.ts b/be/algo-with-me-score/src/score/score.service.spec.ts new file mode 100644 index 0000000..7d85657 --- /dev/null +++ b/be/algo-with-me-score/src/score/score.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ScoreService } from './score.service'; + +describe('ScoreService', () => { + let service: ScoreService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ScoreService], + }).compile(); + + service = module.get (ScoreService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/be/algo-with-me-score/src/score/score.service.ts b/be/algo-with-me-score/src/score/score.service.ts new file mode 100644 index 0000000..d0c1956 --- /dev/null +++ b/be/algo-with-me-score/src/score/score.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ScoreService {} From 85757b876a98d1f90ad1cc1c4721440b36144c58 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 18:54:54 +0900 Subject: [PATCH 042/226] =?UTF-8?q?chore:=20typeorm=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20entity=20=EB=B3=B5=EB=B6=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/package.json | 5 +- be/algo-with-me-score/pnpm-lock.yaml | 404 ++++++++++++++++-- be/algo-with-me-score/src/app.module.ts | 12 +- .../src/score/competition.enums.ts | 7 + .../src/score/entities/problem.entity.ts | 43 ++ .../src/score/entities/submission.entity.ts | 39 ++ .../src/score/score.controller.spec.ts | 18 - .../src/score/score.controller.ts | 7 +- .../src/score/score.service.spec.ts | 18 - be/algo-with-me-score/src/typeorm.config.ts | 14 + be/algo-with-me-score/tsconfig.json | 6 +- 11 files changed, 485 insertions(+), 88 deletions(-) create mode 100644 be/algo-with-me-score/src/score/competition.enums.ts create mode 100644 be/algo-with-me-score/src/score/entities/problem.entity.ts create mode 100644 be/algo-with-me-score/src/score/entities/submission.entity.ts delete mode 100644 be/algo-with-me-score/src/score/score.controller.spec.ts delete mode 100644 be/algo-with-me-score/src/score/score.service.spec.ts create mode 100644 be/algo-with-me-score/src/typeorm.config.ts diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index cccbfa3..b5fd8ad 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -24,9 +24,12 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@nestjs/typeorm": "^10.0.1", "bull": "^4.11.5", + "pg": "^8.11.3", "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "typeorm": "^0.3.17" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index f70db4d..302a6ec 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -17,15 +17,24 @@ dependencies: '@nestjs/platform-express': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) + '@nestjs/typeorm': + specifier: ^10.0.1 + version: 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17) bull: specifier: ^4.11.5 version: 4.11.5 + pg: + specifier: ^8.11.3 + version: 8.11.3 reflect-metadata: specifier: ^0.1.13 version: 0.1.13 rxjs: specifier: ^7.8.1 version: 7.8.1 + typeorm: + specifier: ^0.3.17 + version: 0.3.17(pg@8.11.3)(ts-node@10.9.1) devDependencies: '@nestjs/cli': @@ -451,6 +460,13 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/runtime@7.23.2: + resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: false + /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -503,7 +519,6 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} @@ -820,7 +835,6 @@ packages: /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} @@ -836,7 +850,6 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true /@jridgewell/trace-mapping@0.3.20: resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} @@ -850,7 +863,6 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true /@lukeed/csprng@1.1.0: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} @@ -1071,6 +1083,23 @@ packages: tslib: 2.6.2 dev: true + /@nestjs/typeorm@10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17): + resolution: {integrity: sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 + rxjs: ^7.2.0 + typeorm: ^0.3.0 + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + typeorm: 0.3.17(pg@8.11.3)(ts-node@10.9.1) + uuid: 9.0.1 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1138,21 +1167,21 @@ packages: '@sinonjs/commons': 3.0.0 dev: true + /@sqltools/formatter@1.2.5: + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + dev: false + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - dev: true /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true /@types/babel__core@7.20.4: resolution: {integrity: sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==} @@ -1289,7 +1318,6 @@ packages: resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==} dependencies: undici-types: 5.26.5 - dev: true /@types/qs@6.9.10: resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} @@ -1620,13 +1648,11 @@ packages: /acorn-walk@8.3.0: resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==} engines: {node: '>=0.4.0'} - dev: true /acorn@8.11.2: resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} engines: {node: '>=0.4.0'} hasBin: true - dev: true /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} @@ -1680,7 +1706,6 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -1710,6 +1735,10 @@ packages: engines: {node: '>=12'} dev: true + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -1718,12 +1747,16 @@ packages: picomatch: 2.3.1 dev: true + /app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + dev: false + /append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1896,11 +1929,9 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} @@ -1976,7 +2007,6 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -2012,6 +2042,11 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + /buffer-writer@2.0.0: + resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} + engines: {node: '>=4'} + dev: false + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: @@ -2019,6 +2054,13 @@ packages: ieee754: 1.2.1 dev: true + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /bull@4.11.5: resolution: {integrity: sha512-9jazyvBBYr55IRDkfJh/mJjWiq8NJUMoCC5zTuBX4JhkZvVXegnwsaIa1jr3x9xwSxGvWEhwQ9lt1jlCT5j6pQ==} engines: {node: '>=12'} @@ -2138,6 +2180,19 @@ packages: restore-cursor: 3.1.0 dev: true + /cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + dev: false + /cli-spinners@2.9.1: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} engines: {node: '>=6'} @@ -2157,6 +2212,14 @@ packages: engines: {node: '>= 10'} dev: true + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2164,7 +2227,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -2323,7 +2385,6 @@ packages: /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true /cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} @@ -2341,6 +2402,13 @@ packages: which: 2.0.2 dev: true + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.23.2 + dev: false + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -2475,7 +2543,6 @@ packages: /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - dev: true /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -2498,6 +2565,11 @@ packages: esutils: 2.0.3 dev: true + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: false + /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true @@ -2516,7 +2588,6 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -2622,7 +2693,6 @@ packages: /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - dev: true /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -3190,7 +3260,6 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -3225,7 +3294,6 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} @@ -3312,6 +3380,17 @@ packages: path-is-absolute: 1.0.1 dev: true + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: false + /glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} @@ -3415,6 +3494,10 @@ packages: engines: {node: '>=8'} dev: true + /highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + dev: false + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true @@ -3452,7 +3535,6 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} @@ -3486,7 +3568,6 @@ packages: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3639,7 +3720,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} @@ -4443,7 +4523,6 @@ packages: /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} @@ -4523,6 +4602,13 @@ packages: brace-expansion: 1.1.11 dev: true + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + /minimatch@8.0.4: resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} engines: {node: '>=16 || 14 >=14.17'} @@ -4556,6 +4642,12 @@ packages: dependencies: minimist: 1.2.8 + /mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -4603,6 +4695,14 @@ packages: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -4729,7 +4829,6 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -4828,6 +4927,10 @@ packages: engines: {node: '>=6'} dev: true + /packet-reader@1.0.0: + resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} + dev: false + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4845,6 +4948,20 @@ packages: lines-and-columns: 1.2.4 dev: true + /parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + dependencies: + parse5: 6.0.1 + dev: false + + /parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + dev: false + + /parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + dev: false + /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -4892,6 +5009,70 @@ packages: engines: {node: '>=8'} dev: true + /pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + requiresBuild: true + dev: false + optional: true + + /pg-connection-string@2.6.2: + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + dev: false + + /pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + dev: false + + /pg-pool@3.6.1(pg@8.11.3): + resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==} + peerDependencies: + pg: '>=8.0' + dependencies: + pg: 8.11.3 + dev: false + + /pg-protocol@1.6.0: + resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} + dev: false + + /pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + dev: false + + /pg@8.11.3: + resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + dependencies: + buffer-writer: 2.0.0 + packet-reader: 1.0.0 + pg-connection-string: 2.6.2 + pg-pool: 3.6.1(pg@8.11.3) + pg-protocol: 1.6.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + dev: false + + /pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + dependencies: + split2: 4.2.0 + dev: false + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -4918,6 +5099,28 @@ packages: engines: {node: '>=4'} dev: true + /postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + dev: false + + /postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + dev: false + + /postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + dev: false + + /postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + dependencies: + xtend: 4.0.2 + dev: false + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -5077,6 +5280,10 @@ packages: /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /regenerator-runtime@0.14.0: + resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + dev: false + /regexp.prototype.flags@1.5.1: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} @@ -5094,7 +5301,6 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -5293,6 +5499,14 @@ packages: /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -5364,6 +5578,11 @@ packages: engines: {node: '>= 8'} dev: true + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: false + /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true @@ -5402,7 +5621,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -5454,7 +5672,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} @@ -5607,6 +5824,19 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true @@ -5739,7 +5969,6 @@ packages: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true /tsconfig-paths-webpack-plugin@4.1.0: resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} @@ -5849,11 +6078,89 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + /typeorm@0.3.17(pg@8.11.3)(ts-node@10.9.1): + resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==} + engines: {node: '>= 12.9.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 + '@sap/hana-client': ^2.12.25 + better-sqlite3: ^7.1.2 || ^8.0.0 + hdb-pool: ^0.1.6 + ioredis: ^5.0.4 + mongodb: ^5.2.0 + mssql: ^9.1.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^5.1.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + hdb-pool: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + dependencies: + '@sqltools/formatter': 1.2.5 + app-root-path: 3.1.0 + buffer: 6.0.3 + chalk: 4.1.2 + cli-highlight: 2.1.11 + date-fns: 2.30.0 + debug: 4.3.4 + dotenv: 16.3.1 + glob: 8.1.0 + mkdirp: 2.1.6 + pg: 8.11.3 + reflect-metadata: 0.1.13 + sha.js: 2.4.11 + ts-node: 10.9.1(@types/node@20.9.0)(typescript@5.2.2) + tslib: 2.6.2 + uuid: 9.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: false + /typescript@5.2.2: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true - dev: true /uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} @@ -5872,7 +6179,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} @@ -5917,9 +6223,13 @@ packages: hasBin: true dev: false + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true /v8-to-istanbul@9.1.3: resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} @@ -6065,7 +6375,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} @@ -6078,7 +6387,6 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true /write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} @@ -6095,7 +6403,6 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -6104,10 +6411,27 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: false + /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - dev: true + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: false /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} @@ -6120,12 +6444,10 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} - dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 3598986..1879bd7 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -1,14 +1,14 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { AppController } from '@src/app.controller'; -import { AppService } from '@src/app.service'; import { ScoreController } from './score/score.controller'; -import { ScoreService } from './score/score.service'; import { ScoreModule } from './score/score.module'; +import { ScoreService } from './score/score.service'; +import { typeORMConfig } from './typeorm.config'; @Module({ - imports: [ScoreModule], - controllers: [AppController, ScoreController], - providers: [AppService, ScoreService], + imports: [ScoreModule, TypeOrmModule.forRoot(typeORMConfig)], + controllers: [ScoreController], + providers: [ScoreService], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/score/competition.enums.ts b/be/algo-with-me-score/src/score/competition.enums.ts new file mode 100644 index 0000000..03836b2 --- /dev/null +++ b/be/algo-with-me-score/src/score/competition.enums.ts @@ -0,0 +1,7 @@ +export const RESULT = { + PROGRESS: '처리중', + CORRECT: '정답입니다', + WRONG: '오답입니다.', + TIMEOUT: '시간초과', + OOM: '메모리초과', +} as const; diff --git a/be/algo-with-me-score/src/score/entities/problem.entity.ts b/be/algo-with-me-score/src/score/entities/problem.entity.ts new file mode 100644 index 0000000..1783663 --- /dev/null +++ b/be/algo-with-me-score/src/score/entities/problem.entity.ts @@ -0,0 +1,43 @@ +import { + Column, + CreateDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { Submission } from './submission.entity'; + +@Entity() +export class Problem { + @PrimaryGeneratedColumn() + id: number; + + @Column() + title: string; + + @Column() + timeLimit: number; + + @Column() + memoryLimit: number; + + @Column() + testcaseNum: number; + + @Column('text') + frameCode: string; + + @Column('text') + solutionCode: string; + + @OneToMany(() => Submission, (submission) => submission.problem) + submissions: Submission[]; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/be/algo-with-me-score/src/score/entities/submission.entity.ts b/be/algo-with-me-score/src/score/entities/submission.entity.ts new file mode 100644 index 0000000..d9e97da --- /dev/null +++ b/be/algo-with-me-score/src/score/entities/submission.entity.ts @@ -0,0 +1,39 @@ +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { Problem } from './problem.entity'; +import { RESULT } from '../competition.enums'; + +@Entity() +export class Submission { + @PrimaryGeneratedColumn() + id: number; + + @Column('text') + code: string; + + @Column({ + type: 'enum', + enum: RESULT, + default: RESULT.PROGRESS, + }) + result: string; + + @Column('json', { nullable: true }) + detail: string; + + @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) + problem: Problem; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/be/algo-with-me-score/src/score/score.controller.spec.ts b/be/algo-with-me-score/src/score/score.controller.spec.ts deleted file mode 100644 index 6ad431e..0000000 --- a/be/algo-with-me-score/src/score/score.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ScoreController } from './score.controller'; - -describe('ScoreController', () => { - let controller: ScoreController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [ScoreController], - }).compile(); - - controller = module.get (ScoreController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/be/algo-with-me-score/src/score/score.controller.ts b/be/algo-with-me-score/src/score/score.controller.ts index 5d4b454..960af90 100644 --- a/be/algo-with-me-score/src/score/score.controller.ts +++ b/be/algo-with-me-score/src/score/score.controller.ts @@ -1,4 +1,9 @@ import { Controller } from '@nestjs/common'; @Controller('score') -export class ScoreController {} +export class ScoreController { + // @Post() + // score(@Body('submissionId') submissionId) { + // submissionId; + // } +} diff --git a/be/algo-with-me-score/src/score/score.service.spec.ts b/be/algo-with-me-score/src/score/score.service.spec.ts deleted file mode 100644 index 7d85657..0000000 --- a/be/algo-with-me-score/src/score/score.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ScoreService } from './score.service'; - -describe('ScoreService', () => { - let service: ScoreService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ScoreService], - }).compile(); - - service = module.get (ScoreService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/be/algo-with-me-score/src/typeorm.config.ts b/be/algo-with-me-score/src/typeorm.config.ts new file mode 100644 index 0000000..949feb4 --- /dev/null +++ b/be/algo-with-me-score/src/typeorm.config.ts @@ -0,0 +1,14 @@ +import { TypeOrmModuleOptions } from '@nestjs/typeorm'; + +import * as process from 'process'; + +export const typeORMConfig: TypeOrmModuleOptions = { + type: 'postgres', + host: process.env.DB_HOST, + port: parseInt(process.env.DB_PORT), + username: process.env.DB_USERNAME, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + synchronize: true, + entities: [], +}; diff --git a/be/algo-with-me-score/tsconfig.json b/be/algo-with-me-score/tsconfig.json index f52a0b1..905cbd5 100644 --- a/be/algo-with-me-score/tsconfig.json +++ b/be/algo-with-me-score/tsconfig.json @@ -17,8 +17,8 @@ "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false, - "paths": { - "@src/*": ["./src/*"], - } +// "paths": { +// "@src/*": ["./src/*"], +// } } } From 87077e31f3be4afbe6fec80f424087805f088d05 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 16 Nov 2023 21:44:32 +0900 Subject: [PATCH 043/226] =?UTF-8?q?chore:=20redis=20bull=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/package.json | 1 + be/algo-with-me-score/pnpm-lock.yaml | 4 +- be/algo-with-me-score/src/app.controller.ts | 13 +++++++ be/algo-with-me-score/src/app.module.ts | 38 +++++++++++++++---- be/algo-with-me-score/src/app.service.ts | 13 +++++++ .../src/{score => }/competition.enums.ts | 0 be/algo-with-me-score/src/consumer.ts | 30 +++++++++++++++ .../{score => }/entities/problem.entity.ts | 0 .../{score => }/entities/submission.entity.ts | 0 .../src/score/score.controller.ts | 9 ----- .../src/score/score.module.ts | 4 -- .../src/score/score.service.ts | 4 -- be/algo-with-me-score/src/typeorm.config.ts | 14 ------- 13 files changed, 91 insertions(+), 39 deletions(-) create mode 100644 be/algo-with-me-score/src/app.controller.ts create mode 100644 be/algo-with-me-score/src/app.service.ts rename be/algo-with-me-score/src/{score => }/competition.enums.ts (100%) create mode 100644 be/algo-with-me-score/src/consumer.ts rename be/algo-with-me-score/src/{score => }/entities/problem.entity.ts (100%) rename be/algo-with-me-score/src/{score => }/entities/submission.entity.ts (100%) delete mode 100644 be/algo-with-me-score/src/score/score.controller.ts delete mode 100644 be/algo-with-me-score/src/score/score.module.ts delete mode 100644 be/algo-with-me-score/src/score/score.service.ts delete mode 100644 be/algo-with-me-score/src/typeorm.config.ts diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index b5fd8ad..0a85cd7 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -41,6 +41,7 @@ "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "dotenv": "^16.3.1", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.1", diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index 302a6ec..ece2f2b 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -64,6 +64,9 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.0.0 version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + dotenv: + specifier: ^16.3.1 + version: 16.3.1 eslint: specifier: ^8.42.0 version: 8.53.0 @@ -2568,7 +2571,6 @@ packages: /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} - dev: false /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} diff --git a/be/algo-with-me-score/src/app.controller.ts b/be/algo-with-me-score/src/app.controller.ts new file mode 100644 index 0000000..27be6d8 --- /dev/null +++ b/be/algo-with-me-score/src/app.controller.ts @@ -0,0 +1,13 @@ +import { Body, Controller, Post } from '@nestjs/common'; + +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Post() + addMessage(@Body('data') data: number) { + return this.appService.addMessageQueue(data); + } +} diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 1879bd7..9da7e69 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -1,14 +1,38 @@ +import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import * as dotenv from 'dotenv'; -import { ScoreController } from './score/score.controller'; -import { ScoreModule } from './score/score.module'; -import { ScoreService } from './score/score.service'; -import { typeORMConfig } from './typeorm.config'; +import * as process from 'node:process'; + +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +dotenv.config(); @Module({ - imports: [ScoreModule, TypeOrmModule.forRoot(typeORMConfig)], - controllers: [ScoreController], - providers: [ScoreService], + imports: [ + TypeOrmModule.forRoot({ + type: 'postgres', + host: process.env.DB_HOST, + port: parseInt(process.env.DB_PORT), + username: process.env.DB_USERNAME, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + synchronize: true, + entities: [], + }), + BullModule.forRoot({ + redis: { + host: 'localhost', + port: 6379, + }, + }), + BullModule.registerQueue({ + name: 'testQueue', + }), + ], + controllers: [AppController], + providers: [AppService], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts new file mode 100644 index 0000000..58d5bd8 --- /dev/null +++ b/be/algo-with-me-score/src/app.service.ts @@ -0,0 +1,13 @@ +import { InjectQueue } from '@nestjs/bull'; +import { Injectable } from '@nestjs/common'; +import { Queue } from 'bull'; + +@Injectable() +export class AppService { + constructor(@InjectQueue('testQueue') private testQueue: Queue) {} + + async addMessageQueue(data: number) { + const job = await this.testQueue.add('task', { dataId: data }); + return job; + } +} diff --git a/be/algo-with-me-score/src/score/competition.enums.ts b/be/algo-with-me-score/src/competition.enums.ts similarity index 100% rename from be/algo-with-me-score/src/score/competition.enums.ts rename to be/algo-with-me-score/src/competition.enums.ts diff --git a/be/algo-with-me-score/src/consumer.ts b/be/algo-with-me-score/src/consumer.ts new file mode 100644 index 0000000..0269166 --- /dev/null +++ b/be/algo-with-me-score/src/consumer.ts @@ -0,0 +1,30 @@ +import { Process, Processor } from '@nestjs/bull'; +import { Logger } from '@nestjs/common'; +import { Job } from 'bull'; + +@Processor('testQueue') +export class AppConsumer { + private readonly logger = new Logger(AppConsumer.name); + + @Process('task') + getMessageQueue(job: Job) { + this.logger.log(`${job.data}.dataId번 작업을 수행함`); + } + + // @Process() + // async transcode(job: Job ) { + // console.log(job.data); + // for (let i = 0; i < 10; i++) { + // console.log(i); + // } + // return { good: 'good' }; + // } + // + // @OnQueueCompleted() + // onCompleted(job: Job , result: any) { + // console.log('job done'); + // console.log(result); + // // redis 에서 데이터 삭제 + // job.remove(); + // } +} diff --git a/be/algo-with-me-score/src/score/entities/problem.entity.ts b/be/algo-with-me-score/src/entities/problem.entity.ts similarity index 100% rename from be/algo-with-me-score/src/score/entities/problem.entity.ts rename to be/algo-with-me-score/src/entities/problem.entity.ts diff --git a/be/algo-with-me-score/src/score/entities/submission.entity.ts b/be/algo-with-me-score/src/entities/submission.entity.ts similarity index 100% rename from be/algo-with-me-score/src/score/entities/submission.entity.ts rename to be/algo-with-me-score/src/entities/submission.entity.ts diff --git a/be/algo-with-me-score/src/score/score.controller.ts b/be/algo-with-me-score/src/score/score.controller.ts deleted file mode 100644 index 960af90..0000000 --- a/be/algo-with-me-score/src/score/score.controller.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('score') -export class ScoreController { - // @Post() - // score(@Body('submissionId') submissionId) { - // submissionId; - // } -} diff --git a/be/algo-with-me-score/src/score/score.module.ts b/be/algo-with-me-score/src/score/score.module.ts deleted file mode 100644 index 082e101..0000000 --- a/be/algo-with-me-score/src/score/score.module.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Module } from '@nestjs/common'; - -@Module({}) -export class ScoreModule {} diff --git a/be/algo-with-me-score/src/score/score.service.ts b/be/algo-with-me-score/src/score/score.service.ts deleted file mode 100644 index d0c1956..0000000 --- a/be/algo-with-me-score/src/score/score.service.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class ScoreService {} diff --git a/be/algo-with-me-score/src/typeorm.config.ts b/be/algo-with-me-score/src/typeorm.config.ts deleted file mode 100644 index 949feb4..0000000 --- a/be/algo-with-me-score/src/typeorm.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TypeOrmModuleOptions } from '@nestjs/typeorm'; - -import * as process from 'process'; - -export const typeORMConfig: TypeOrmModuleOptions = { - type: 'postgres', - host: process.env.DB_HOST, - port: parseInt(process.env.DB_PORT), - username: process.env.DB_USERNAME, - password: process.env.DB_PASSWORD, - database: process.env.DB_NAME, - synchronize: true, - entities: [], -}; From 9a491e1211177c5c17cda24ff1de0c5b17d4ea96 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 17 Nov 2023 19:13:27 +0900 Subject: [PATCH 044/226] =?UTF-8?q?feat:=20=EC=B1=84=EC=A0=90=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(?= =?UTF-8?q?=EC=8B=A4=EC=A0=9C=20=EC=8B=9C=EC=8A=A4=ED=85=9C=EC=97=90=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=ED=95=98=EB=A0=A4=EB=A9=B4=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=95=84=EC=9A=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/package.json | 3 +- be/algo-with-me-score/pnpm-lock.yaml | 34 +++- be/algo-with-me-score/src/app.controller.ts | 4 +- be/algo-with-me-score/src/app.module.ts | 14 +- be/algo-with-me-score/src/app.service.ts | 2 +- be/algo-with-me-score/src/consumer.ts | 30 ---- .../src/entities/submission.entity.ts | 2 +- .../submission.enums.ts} | 0 .../interfaces/coderun-response.interface.ts | 8 + .../src/interfaces/score-result.interface.ts | 16 ++ .../src/services/score.consumer.ts | 170 ++++++++++++++++++ 11 files changed, 239 insertions(+), 44 deletions(-) delete mode 100644 be/algo-with-me-score/src/consumer.ts rename be/algo-with-me-score/src/{competition.enums.ts => entities/submission.enums.ts} (100%) create mode 100644 be/algo-with-me-score/src/interfaces/coderun-response.interface.ts create mode 100644 be/algo-with-me-score/src/interfaces/score-result.interface.ts create mode 100644 be/algo-with-me-score/src/services/score.consumer.ts diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index 0a85cd7..4b2b726 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -22,10 +22,12 @@ "dependencies": { "@nestjs/bull": "^10.0.1", "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.1", "bull": "^4.11.5", + "dotenv": "^16.3.1", "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", @@ -41,7 +43,6 @@ "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", - "dotenv": "^16.3.1", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.1", diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index ece2f2b..7e4b163 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@nestjs/common': specifier: ^10.0.0 version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/config': + specifier: ^3.1.1 + version: 3.1.1(@nestjs/common@10.2.8)(reflect-metadata@0.1.13) '@nestjs/core': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) @@ -23,6 +26,9 @@ dependencies: bull: specifier: ^4.11.5 version: 4.11.5 + dotenv: + specifier: ^16.3.1 + version: 16.3.1 pg: specifier: ^8.11.3 version: 8.11.3 @@ -64,9 +70,6 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.0.0 version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) - dotenv: - specifier: ^16.3.1 - version: 16.3.1 eslint: specifier: ^8.42.0 version: 8.53.0 @@ -1005,6 +1008,20 @@ packages: tslib: 2.6.2 uid: 2.0.2 + /@nestjs/config@3.1.1(@nestjs/common@10.2.8)(reflect-metadata@0.1.13): + resolution: {integrity: sha512-qu5QlNiJdqQtOsnB6lx4JCXPQ96jkKUsOGd+JXfXwqJqZcOSAq6heNFg0opW4pq4J/VZoNwoo87TNnx9wthnqQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + dotenv: 16.3.1 + dotenv-expand: 10.0.0 + lodash: 4.17.21 + reflect-metadata: 0.1.13 + uuid: 9.0.0 + dev: false + /@nestjs/core@10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1): resolution: {integrity: sha512-9+MZ2s8ixfY9Bl/M9ofChiyYymcwdK9ZWNH4GDMF7Am7XRAQ1oqde6MYGG05rhQwiVXuTwaYLlXciJKfsrg5qg==} requiresBuild: true @@ -2568,9 +2585,15 @@ packages: esutils: 2.0.3 dev: true + /dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dev: false + /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} + dev: false /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -6225,6 +6248,11 @@ packages: hasBin: true dev: false + /uuid@9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true diff --git a/be/algo-with-me-score/src/app.controller.ts b/be/algo-with-me-score/src/app.controller.ts index 27be6d8..39decf9 100644 --- a/be/algo-with-me-score/src/app.controller.ts +++ b/be/algo-with-me-score/src/app.controller.ts @@ -7,7 +7,7 @@ export class AppController { constructor(private readonly appService: AppService) {} @Post() - addMessage(@Body('data') data: number) { - return this.appService.addMessageQueue(data); + addMessage(@Body('submissionId') submissionId: number) { + return this.appService.addMessageQueue(submissionId); } } diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 9da7e69..b586cc9 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -5,8 +5,8 @@ import * as dotenv from 'dotenv'; import * as process from 'node:process'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { Submission } from './entities/submission.entity'; +import { SubmissionConsumer } from './services/score.consumer'; dotenv.config(); @@ -22,17 +22,19 @@ dotenv.config(); synchronize: true, entities: [], }), + TypeOrmModule.forFeature([Submission]), BullModule.forRoot({ redis: { - host: 'localhost', - port: 6379, + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, }, }), BullModule.registerQueue({ name: 'testQueue', }), ], - controllers: [AppController], - providers: [AppService], + controllers: [], + providers: [SubmissionConsumer], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts index 58d5bd8..4789a20 100644 --- a/be/algo-with-me-score/src/app.service.ts +++ b/be/algo-with-me-score/src/app.service.ts @@ -7,7 +7,7 @@ export class AppService { constructor(@InjectQueue('testQueue') private testQueue: Queue) {} async addMessageQueue(data: number) { - const job = await this.testQueue.add('task', { dataId: data }); + const job = await this.testQueue.add('score', { submissionId: data }); return job; } } diff --git a/be/algo-with-me-score/src/consumer.ts b/be/algo-with-me-score/src/consumer.ts deleted file mode 100644 index 0269166..0000000 --- a/be/algo-with-me-score/src/consumer.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Process, Processor } from '@nestjs/bull'; -import { Logger } from '@nestjs/common'; -import { Job } from 'bull'; - -@Processor('testQueue') -export class AppConsumer { - private readonly logger = new Logger(AppConsumer.name); - - @Process('task') - getMessageQueue(job: Job) { - this.logger.log(`${job.data}.dataId번 작업을 수행함`); - } - - // @Process() - // async transcode(job: Job ) { - // console.log(job.data); - // for (let i = 0; i < 10; i++) { - // console.log(i); - // } - // return { good: 'good' }; - // } - // - // @OnQueueCompleted() - // onCompleted(job: Job , result: any) { - // console.log('job done'); - // console.log(result); - // // redis 에서 데이터 삭제 - // job.remove(); - // } -} diff --git a/be/algo-with-me-score/src/entities/submission.entity.ts b/be/algo-with-me-score/src/entities/submission.entity.ts index d9e97da..b675d43 100644 --- a/be/algo-with-me-score/src/entities/submission.entity.ts +++ b/be/algo-with-me-score/src/entities/submission.entity.ts @@ -8,7 +8,7 @@ import { } from 'typeorm'; import { Problem } from './problem.entity'; -import { RESULT } from '../competition.enums'; +import { RESULT } from './submission.enums'; @Entity() export class Submission { diff --git a/be/algo-with-me-score/src/competition.enums.ts b/be/algo-with-me-score/src/entities/submission.enums.ts similarity index 100% rename from be/algo-with-me-score/src/competition.enums.ts rename to be/algo-with-me-score/src/entities/submission.enums.ts diff --git a/be/algo-with-me-score/src/interfaces/coderun-response.interface.ts b/be/algo-with-me-score/src/interfaces/coderun-response.interface.ts new file mode 100644 index 0000000..d00e44b --- /dev/null +++ b/be/algo-with-me-score/src/interfaces/coderun-response.interface.ts @@ -0,0 +1,8 @@ +interface ICoderunResponse { + result: 'SUCCESS' | string; + competitionId: number; + userId: number; + problemId: number; +} + +export default ICoderunResponse; diff --git a/be/algo-with-me-score/src/interfaces/score-result.interface.ts b/be/algo-with-me-score/src/interfaces/score-result.interface.ts new file mode 100644 index 0000000..8209aaa --- /dev/null +++ b/be/algo-with-me-score/src/interfaces/score-result.interface.ts @@ -0,0 +1,16 @@ +import { RESULT } from '../entities/submission.enums'; + +interface IScoreResult { + submissionId: number; + competitionId: number; + userId: number; + problemId: number; + testcaseNo: number; + result: keyof typeof RESULT; + stdout: string | Buffer; + stderr: string | Buffer; + timeUsage: number; + memoryUsage: number; +} + +export default IScoreResult; diff --git a/be/algo-with-me-score/src/services/score.consumer.ts b/be/algo-with-me-score/src/services/score.consumer.ts new file mode 100644 index 0000000..efece6e --- /dev/null +++ b/be/algo-with-me-score/src/services/score.consumer.ts @@ -0,0 +1,170 @@ +import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +import { Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Job } from 'bull'; +import { Repository } from 'typeorm'; + +import fs from 'node:fs'; +import * as process from 'process'; + +import { Submission } from '../entities/submission.entity'; +import { RESULT } from '../entities/submission.enums'; +import ICoderunResponse from '../interfaces/coderun-response.interface'; +import IScoreResult from '../interfaces/score-result.interface'; + +@Processor('testQueue') +export class SubmissionConsumer { + constructor( + @InjectRepository(Submission) private readonly submissionRepository: Repository , + ) {} + private readonly logger = new Logger(SubmissionConsumer.name); + + @Process('score') + async getMessageQueue(job: Job) { + this.logger.debug(JSON.stringify(job.data)); + this.logger.debug(`submissionId: ${job.data.submissionId}`); + + const submissionId = job.data.submissionId; + const submission = await this.submissionRepository.findOneBy({ id: submissionId }); + + const { competitionId, userId, problemId } = this.getIds(submission); + this.writeSubmittedCodeToFilesystem(competitionId, userId, problemId, submission); + + // codeRunResult와 testcase의 result를 비교하여, 채점 결과 도출. + // 채점 결과를 아래 sendJudgment에 보내줌 + const scoreResults: IScoreResult[] = []; + for (let testcaseNo = 0; testcaseNo < submission.problem.testcaseNum; testcaseNo++) { + await this.score(submissionId, competitionId, userId, problemId, testcaseNo, scoreResults); + } + + await this.sendScoreResults(submissionId, scoreResults); + } + + private getIds(submission: Submission) { + // submission에 competitionId와 userId가 있어야 할듯 해요 + const competitionId = 1; + const userId = 2; + const problemId = submission.problem.id; + return { competitionId, userId, problemId }; + } + + private async score( + submissionId: number, + competitionId: number, + userId: number, + problemId: number, + testcaseNo: number, + scoreResults: IScoreResult[], + ) { + const codeRunResponse = await this.runCode(competitionId, userId, problemId, testcaseNo); + + const { + result: codeRunOutput, + stdout, + stderr, + } = this.getCodeRunOutputs(competitionId, userId, problemId, testcaseNo); + const testcaseAnswer = this.getTestcaseAnswer(problemId, testcaseNo); + const scoreResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); + + scoreResults.push({ + submissionId, + competitionId, + userId, + problemId, + testcaseNo, + result: scoreResult, + stdout, + stderr, + timeUsage: null, + memoryUsage: null, + }); + } + + private getTestcaseAnswer(problemId: number, testcaseNo: number) { + const testcaseAnswer = fs + .readFileSync(`${process.env.TESTCASE_PATH}/${problemId}/secrets/${testcaseNo}.ans`) + .toString(); + return testcaseAnswer; + } + + private judge( + codeRunResponse: ICoderunResponse, + codeRunOutput: string, + testcaseAnswer: string, + ): keyof typeof RESULT { + if (codeRunResponse.result === 'TIMEOUT') { + return 'TIMEOUT'; + } else if (codeRunResponse.result !== 'SUCCESS') { + return 'WRONG'; + } else if (codeRunOutput === testcaseAnswer) { + return 'CORRECT'; + } else { + return 'WRONG'; + } + } + + private async sendScoreResults(submissionId: number, codeRunResult: IScoreResult[]) { + // API 서버에게 fetch + // API 정하기 필요 + await fetch(`http://localhost:3000/___UNKNOWN_API___`, { + method: 'POST', + body: JSON.stringify({ + submissionId, + codeRunResult, + }), + }); + } + + private getCodeRunOutputs( + competitionId: number, + userId: number, + problemId: number, + testcaseNo: number, + ): { result: string; stdout: string; stderr: string } { + // testcase 번호도 읽는 방식으로 바꿔야 함. + // 이를 위해서는 docker에서 파일을 쓰는 경로도 바꿔야 함 + const submissionBasePath = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/`; + const result = fs.readFileSync(`${submissionBasePath}/${problemId}.result`).toString(); + const stdout = fs.readFileSync(`${submissionBasePath}/${problemId}.stdout`).toString(); + const stderr = fs.readFileSync(`${submissionBasePath}/${problemId}.stderr`).toString(); + return { result, stdout, stderr }; + } + + private async runCode( + competitionId: number, + userId: number, + problemId: number, + testcaseNo: number, + ): Promise { + // 도커 컨테이너에게 fetch + // testcaseNo도 넘겨주는 식으로 바꿔야 할듯, 현재 docker 구현은 아래 3개만 넘겨주는 것으로 되어 있음 + const response = await fetch(`http://localhost:2000/${competitionId}/${userId}/${problemId}`, { + method: 'POST', + }); + return (await response.json()) as ICoderunResponse; + } + + private writeSubmittedCodeToFilesystem( + competitionId: number, + userId: number, + problemId: number, + submission: Submission, + ) { + // code와 frameCode를 어떻게 합칠 것인지 약속해야 함 + const mergedCode = this.getMergedCode(submission.code, submission.problem.frameCode); + fs.writeFileSync( + `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}`, + mergedCode, + ); + } + + private getMergedCode(code: string, frameCode: string) { + const mergedCode = code + frameCode; + return mergedCode; + } + + @OnQueueCompleted() + async onCompleted(job: Job) { + await job.remove(); + } +} From 87c6c977dde08d8870d1950479444237d8565ec8 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 16:14:00 +0900 Subject: [PATCH 045/226] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D,=20log=20?= =?UTF-8?q?=EC=A7=80=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/package.json | 1 - be/algo-with-me-score/pnpm-lock.yaml | 3 - be/algo-with-me-score/src/app.module.ts | 24 +-- .../{ => score}/entities/problem.entity.ts | 0 .../{ => score}/entities/submission.entity.ts | 0 .../{ => score}/entities/submission.enums.ts | 0 .../interfaces/coderun-response.interface.ts | 0 .../interfaces/score-result.interface.ts | 0 .../src/score/score.module.ts | 20 +++ .../src/score/services/filesystem.service.ts | 24 +++ .../src/score/services/score.consumer.ts | 61 ++++++++ .../services/score.service.ts} | 148 ++++++------------ 12 files changed, 161 insertions(+), 120 deletions(-) rename be/algo-with-me-score/src/{ => score}/entities/problem.entity.ts (100%) rename be/algo-with-me-score/src/{ => score}/entities/submission.entity.ts (100%) rename be/algo-with-me-score/src/{ => score}/entities/submission.enums.ts (100%) rename be/algo-with-me-score/src/{ => score}/interfaces/coderun-response.interface.ts (100%) rename be/algo-with-me-score/src/{ => score}/interfaces/score-result.interface.ts (100%) create mode 100644 be/algo-with-me-score/src/score/score.module.ts create mode 100644 be/algo-with-me-score/src/score/services/filesystem.service.ts create mode 100644 be/algo-with-me-score/src/score/services/score.consumer.ts rename be/algo-with-me-score/src/{services/score.consumer.ts => score/services/score.service.ts} (51%) diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index 4b2b726..3d4b070 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -27,7 +27,6 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.1", "bull": "^4.11.5", - "dotenv": "^16.3.1", "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index 7e4b163..e78375c 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -26,9 +26,6 @@ dependencies: bull: specifier: ^4.11.5 version: 4.11.5 - dotenv: - specifier: ^16.3.1 - version: 16.3.1 pg: specifier: ^8.11.3 version: 8.11.3 diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index b586cc9..b636e71 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -1,17 +1,20 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; -import * as dotenv from 'dotenv'; import * as process from 'node:process'; -import { Submission } from './entities/submission.entity'; -import { SubmissionConsumer } from './services/score.consumer'; - -dotenv.config(); +import { Problem } from './score/entities/problem.entity'; +import { Submission } from './score/entities/submission.entity'; +import { ScoreModule } from './score/score.module'; @Module({ imports: [ + ConfigModule.forRoot({ + cache: true, + isGlobal: true, + }), TypeOrmModule.forRoot({ type: 'postgres', host: process.env.DB_HOST, @@ -19,10 +22,9 @@ dotenv.config(); username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, - synchronize: true, - entities: [], + synchronize: false, + entities: [Submission, Problem], }), - TypeOrmModule.forFeature([Submission]), BullModule.forRoot({ redis: { host: process.env.REDIS_HOST, @@ -30,11 +32,9 @@ dotenv.config(); password: process.env.REDIS_PASSWORD, }, }), - BullModule.registerQueue({ - name: 'testQueue', - }), + ScoreModule, ], controllers: [], - providers: [SubmissionConsumer], + providers: [], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/entities/problem.entity.ts b/be/algo-with-me-score/src/score/entities/problem.entity.ts similarity index 100% rename from be/algo-with-me-score/src/entities/problem.entity.ts rename to be/algo-with-me-score/src/score/entities/problem.entity.ts diff --git a/be/algo-with-me-score/src/entities/submission.entity.ts b/be/algo-with-me-score/src/score/entities/submission.entity.ts similarity index 100% rename from be/algo-with-me-score/src/entities/submission.entity.ts rename to be/algo-with-me-score/src/score/entities/submission.entity.ts diff --git a/be/algo-with-me-score/src/entities/submission.enums.ts b/be/algo-with-me-score/src/score/entities/submission.enums.ts similarity index 100% rename from be/algo-with-me-score/src/entities/submission.enums.ts rename to be/algo-with-me-score/src/score/entities/submission.enums.ts diff --git a/be/algo-with-me-score/src/interfaces/coderun-response.interface.ts b/be/algo-with-me-score/src/score/interfaces/coderun-response.interface.ts similarity index 100% rename from be/algo-with-me-score/src/interfaces/coderun-response.interface.ts rename to be/algo-with-me-score/src/score/interfaces/coderun-response.interface.ts diff --git a/be/algo-with-me-score/src/interfaces/score-result.interface.ts b/be/algo-with-me-score/src/score/interfaces/score-result.interface.ts similarity index 100% rename from be/algo-with-me-score/src/interfaces/score-result.interface.ts rename to be/algo-with-me-score/src/score/interfaces/score-result.interface.ts diff --git a/be/algo-with-me-score/src/score/score.module.ts b/be/algo-with-me-score/src/score/score.module.ts new file mode 100644 index 0000000..c827154 --- /dev/null +++ b/be/algo-with-me-score/src/score/score.module.ts @@ -0,0 +1,20 @@ +import { BullModule } from '@nestjs/bull'; +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { Submission } from './entities/submission.entity'; +import { FilesystemService } from './services/filesystem.service'; +import { SubmissionConsumer } from './services/score.consumer'; +import { ScoreService } from './services/score.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Submission]), + BullModule.registerQueue({ + name: process.env.REDIS_MESSAGE_QUEUE_NAME, + }), + ], + controllers: [], + providers: [SubmissionConsumer, FilesystemService, ScoreService], +}) +export class ScoreModule {} diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts new file mode 100644 index 0000000..741b067 --- /dev/null +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -0,0 +1,24 @@ +import fs from 'node:fs'; +import process from 'process'; + +import { Submission } from '../entities/submission.entity'; + +export class FilesystemService { + public writeSubmittedCode( + competitionId: number, + userId: number, + problemId: number, + submission: Submission, + ) { + const mergedCode = this.getMergedCode(submission.code, submission.problem.frameCode); + fs.writeFileSync( + `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}`, + mergedCode, + ); + } + + private getMergedCode(code: string, frameCode: string) { + const mergedCode = code + frameCode; + return mergedCode; + } +} diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts new file mode 100644 index 0000000..4a691dc --- /dev/null +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -0,0 +1,61 @@ +import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Job } from 'bull'; +import { Repository } from 'typeorm'; + +import * as process from 'process'; + +import { FilesystemService } from './filesystem.service'; +import { ScoreService } from './score.service'; +import { Submission } from '../entities/submission.entity'; +import IScoreResult from '../interfaces/score-result.interface'; + +@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +export class SubmissionConsumer { + constructor( + @InjectRepository(Submission) private readonly submissionRepository: Repository , + private readonly filesystemService: FilesystemService, + private readonly scoreService: ScoreService, + ) {} + + @Process('score') + async getMessageQueue(job: Job) { + const submissionId = job.data.submissionId; + const submission = await this.submissionRepository.findOneBy({ id: submissionId }); + const { competitionId, userId, problemId } = this.getIds(submission); + + this.filesystemService.writeSubmittedCode(competitionId, userId, problemId, submission); + + const scoreResults = await this.scoreService.score( + submission, + submissionId, + competitionId, + userId, + problemId, + ); + + await this.sendScoreResults(submissionId, scoreResults); + } + + private getIds(submission: Submission) { + const competitionId = 1; + const userId = 2; + const problemId = submission.problem.id; + return { competitionId, userId, problemId }; + } + + private async sendScoreResults(submissionId: number, codeRunResult: IScoreResult[]) { + await fetch(`http://localhost:3000/scores`, { + method: 'POST', + body: JSON.stringify({ + submissionId, + codeRunResult, + }), + }); + } + + @OnQueueCompleted() + async onCompleted(job: Job) { + await job.remove(); + } +} diff --git a/be/algo-with-me-score/src/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.service.ts similarity index 51% rename from be/algo-with-me-score/src/services/score.consumer.ts rename to be/algo-with-me-score/src/score/services/score.service.ts index efece6e..03f1bfc 100644 --- a/be/algo-with-me-score/src/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -1,54 +1,34 @@ -import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Job } from 'bull'; -import { Repository } from 'typeorm'; - import fs from 'node:fs'; -import * as process from 'process'; +import process from 'process'; import { Submission } from '../entities/submission.entity'; import { RESULT } from '../entities/submission.enums'; import ICoderunResponse from '../interfaces/coderun-response.interface'; import IScoreResult from '../interfaces/score-result.interface'; -@Processor('testQueue') -export class SubmissionConsumer { - constructor( - @InjectRepository(Submission) private readonly submissionRepository: Repository , - ) {} - private readonly logger = new Logger(SubmissionConsumer.name); - - @Process('score') - async getMessageQueue(job: Job) { - this.logger.debug(JSON.stringify(job.data)); - this.logger.debug(`submissionId: ${job.data.submissionId}`); - - const submissionId = job.data.submissionId; - const submission = await this.submissionRepository.findOneBy({ id: submissionId }); - - const { competitionId, userId, problemId } = this.getIds(submission); - this.writeSubmittedCodeToFilesystem(competitionId, userId, problemId, submission); - - // codeRunResult와 testcase의 result를 비교하여, 채점 결과 도출. - // 채점 결과를 아래 sendJudgment에 보내줌 +export class ScoreService { + public async score( + submission: Submission, + submissionId: number, + competitionId: number, + userId: number, + problemId: number, + ) { const scoreResults: IScoreResult[] = []; for (let testcaseNo = 0; testcaseNo < submission.problem.testcaseNum; testcaseNo++) { - await this.score(submissionId, competitionId, userId, problemId, testcaseNo, scoreResults); + await this.scoreOneTestcase( + submissionId, + competitionId, + userId, + problemId, + testcaseNo, + scoreResults, + ); } - - await this.sendScoreResults(submissionId, scoreResults); + return scoreResults; } - private getIds(submission: Submission) { - // submission에 competitionId와 userId가 있어야 할듯 해요 - const competitionId = 1; - const userId = 2; - const problemId = submission.problem.id; - return { competitionId, userId, problemId }; - } - - private async score( + private async scoreOneTestcase( submissionId: number, competitionId: number, userId: number, @@ -80,6 +60,31 @@ export class SubmissionConsumer { }); } + private async runCode( + competitionId: number, + userId: number, + problemId: number, + testcaseNo: number, + ): Promise { + const response = await fetch(`http://localhost:2000/${competitionId}/${userId}/${problemId}`, { + method: 'POST', + }); + return (await response.json()) as ICoderunResponse; + } + + private getCodeRunOutputs( + competitionId: number, + userId: number, + problemId: number, + testcaseNo: number, + ): { result: string; stdout: string; stderr: string } { + const submissionBasePath = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/`; + const result = fs.readFileSync(`${submissionBasePath}/${problemId}.result`).toString(); + const stdout = fs.readFileSync(`${submissionBasePath}/${problemId}.stdout`).toString(); + const stderr = fs.readFileSync(`${submissionBasePath}/${problemId}.stderr`).toString(); + return { result, stdout, stderr }; + } + private getTestcaseAnswer(problemId: number, testcaseNo: number) { const testcaseAnswer = fs .readFileSync(`${process.env.TESTCASE_PATH}/${problemId}/secrets/${testcaseNo}.ans`) @@ -102,69 +107,4 @@ export class SubmissionConsumer { return 'WRONG'; } } - - private async sendScoreResults(submissionId: number, codeRunResult: IScoreResult[]) { - // API 서버에게 fetch - // API 정하기 필요 - await fetch(`http://localhost:3000/___UNKNOWN_API___`, { - method: 'POST', - body: JSON.stringify({ - submissionId, - codeRunResult, - }), - }); - } - - private getCodeRunOutputs( - competitionId: number, - userId: number, - problemId: number, - testcaseNo: number, - ): { result: string; stdout: string; stderr: string } { - // testcase 번호도 읽는 방식으로 바꿔야 함. - // 이를 위해서는 docker에서 파일을 쓰는 경로도 바꿔야 함 - const submissionBasePath = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/`; - const result = fs.readFileSync(`${submissionBasePath}/${problemId}.result`).toString(); - const stdout = fs.readFileSync(`${submissionBasePath}/${problemId}.stdout`).toString(); - const stderr = fs.readFileSync(`${submissionBasePath}/${problemId}.stderr`).toString(); - return { result, stdout, stderr }; - } - - private async runCode( - competitionId: number, - userId: number, - problemId: number, - testcaseNo: number, - ): Promise { - // 도커 컨테이너에게 fetch - // testcaseNo도 넘겨주는 식으로 바꿔야 할듯, 현재 docker 구현은 아래 3개만 넘겨주는 것으로 되어 있음 - const response = await fetch(`http://localhost:2000/${competitionId}/${userId}/${problemId}`, { - method: 'POST', - }); - return (await response.json()) as ICoderunResponse; - } - - private writeSubmittedCodeToFilesystem( - competitionId: number, - userId: number, - problemId: number, - submission: Submission, - ) { - // code와 frameCode를 어떻게 합칠 것인지 약속해야 함 - const mergedCode = this.getMergedCode(submission.code, submission.problem.frameCode); - fs.writeFileSync( - `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}`, - mergedCode, - ); - } - - private getMergedCode(code: string, frameCode: string) { - const mergedCode = code + frameCode; - return mergedCode; - } - - @OnQueueCompleted() - async onCompleted(job: Job) { - await job.remove(); - } } From 6a6e7ded97d167cdef7e8d02318d129b9add7704 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Mon, 20 Nov 2023 18:29:40 +0900 Subject: [PATCH 046/226] =?UTF-8?q?chore:=20github=20oauth=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 3 ++ be/algo-with-me-api/pnpm-lock.yaml | 68 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index fa6c14f..51d92e6 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -25,6 +25,7 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", + "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^10.2.8", "@nestjs/typeorm": "^10.0.0", @@ -32,6 +33,8 @@ "bull": "^4.11.5", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "passport": "^0.6.0", + "passport-github": "^1.1.0", "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index f872a1b..65269b5 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: '@nestjs/mapped-types': specifier: '*' version: 2.0.3(@nestjs/common@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) + '@nestjs/passport': + specifier: ^10.0.2 + version: 10.0.2(@nestjs/common@10.2.8)(passport@0.6.0) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) @@ -41,6 +44,12 @@ dependencies: class-validator: specifier: ^0.14.0 version: 0.14.0 + passport: + specifier: ^0.6.0 + version: 0.6.0 + passport-github: + specifier: ^1.1.0 + version: 1.1.0 pg: specifier: ^8.11.3 version: 8.11.3 @@ -1092,6 +1101,16 @@ packages: class-validator: 0.14.0 reflect-metadata: 0.1.13 + /@nestjs/passport@10.0.2(@nestjs/common@10.2.8)(passport@0.6.0): + resolution: {integrity: sha512-od31vfB2z3y05IDB5dWSbCGE2+pAf2k2WCBinNuTTOxN0O0+wtO1L3kawj/aCW3YR9uxsTOVbTDwtwgpNNsnjQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + passport: ^0.4.0 || ^0.5.0 || ^0.6.0 + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + passport: 0.6.0 + dev: false + /@nestjs/platform-express@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): resolution: {integrity: sha512-WoSSVtwIRc5AdGMHWVzWZK4JZLT0f4o2xW8P9gQvcX+omL8W1kXCfY8GQYXNBG84XmBNYH8r0FtC8oMe/lH5NQ==} peerDependencies: @@ -2077,6 +2096,11 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + /base64url@3.0.1: + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} + dev: false + /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} @@ -4959,6 +4983,10 @@ packages: path-key: 4.0.0 dev: true + /oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -5159,6 +5187,38 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + /passport-github@1.1.0: + resolution: {integrity: sha512-XARXJycE6fFh/dxF+Uut8OjlwbFEXgbPVj/+V+K7cvriRK7VcAOm+NgBmbiLM9Qv3SSxEAV+V6fIk89nYHXa8A==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-oauth2: 1.7.0 + dev: false + + /passport-oauth2@1.7.0: + resolution: {integrity: sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==} + engines: {node: '>= 0.4.0'} + dependencies: + base64url: 3.0.1 + oauth: 0.9.15 + passport-strategy: 1.0.0 + uid2: 0.0.4 + utils-merge: 1.0.1 + dev: false + + /passport-strategy@1.0.0: + resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} + engines: {node: '>= 0.4.0'} + dev: false + + /passport@0.6.0: + resolution: {integrity: sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + dev: false + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5202,6 +5262,10 @@ packages: engines: {node: '>=8'} dev: true + /pause@0.0.1: + resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} + dev: false + /pg-cloudflare@1.1.1: resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} requiresBuild: true @@ -6392,6 +6456,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + /uid2@0.0.4: + resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} + dev: false + /uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} engines: {node: '>=8'} From 21089f980a37ebfa7312a48d88786ecb7c04dfad Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Mon, 20 Nov 2023 18:34:11 +0900 Subject: [PATCH 047/226] =?UTF-8?q?feat:=20suer,=20auth=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 4 ++++ be/algo-with-me-api/src/auth/auth.module.ts | 4 ++++ be/algo-with-me-api/src/user/user.module.ts | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 be/algo-with-me-api/src/auth/auth.module.ts create mode 100644 be/algo-with-me-api/src/user/user.module.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 481626a..11f82a8 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -3,9 +3,11 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { AuthModule } from './auth/auth.module'; import { CompetitionModule } from './competition/competition.module'; import { Problem } from './competition/entities/problem.entity'; import { Submission } from './competition/entities/submission.entity'; +import { UserModule } from './user/user.module'; @Module({ imports: [ @@ -31,6 +33,8 @@ import { Submission } from './competition/entities/submission.entity'; }, }), CompetitionModule, + AuthModule, + UserModule, ], }) export class AppModule {} diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts new file mode 100644 index 0000000..7459c06 --- /dev/null +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -0,0 +1,4 @@ +import { Module } from '@nestjs/common'; + +@Module({}) +export class AuthModule {} diff --git a/be/algo-with-me-api/src/user/user.module.ts b/be/algo-with-me-api/src/user/user.module.ts new file mode 100644 index 0000000..309e84a --- /dev/null +++ b/be/algo-with-me-api/src/user/user.module.ts @@ -0,0 +1,4 @@ +import { Module } from '@nestjs/common'; + +@Module({}) +export class UserModule {} From 0ce5c14c899d851bf9dc31569a2b6ae8fe2b9bf6 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 17:23:34 +0900 Subject: [PATCH 048/226] =?UTF-8?q?feat:=20competition=20entity=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A4=EA=B3=A0=20=EA=B4=80=EA=B3=84=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 4 +- .../src/competition/competition.module.ts | 4 +- .../entities/competition.entity.ts | 66 +++++++++++++++++++ .../competition/entities/problem.entity.ts | 12 ++++ .../competition/entities/submission.entity.ts | 4 ++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/entities/competition.entity.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 481626a..8f005f6 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -7,6 +7,8 @@ import { CompetitionModule } from './competition/competition.module'; import { Problem } from './competition/entities/problem.entity'; import { Submission } from './competition/entities/submission.entity'; +import { Competition } from '@src/competition/entities/competition.entity'; + @Module({ imports: [ ConfigModule.forRoot({ @@ -21,7 +23,7 @@ import { Submission } from './competition/entities/submission.entity'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem, Submission], + entities: [Problem, Submission, Competition], }), BullModule.forRoot({ redis: { diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 4304686..6de3de7 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -11,9 +11,11 @@ import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; import { SubmissionConsumer } from './tem.consumer'; +import { Competition } from '@src/competition/entities/competition.entity'; + @Module({ imports: [ - TypeOrmModule.forFeature([Problem, Submission]), + TypeOrmModule.forFeature([Problem, Submission, Competition]), BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts new file mode 100644 index 0000000..5d28367 --- /dev/null +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -0,0 +1,66 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + Column, + CreateDateColumn, + Entity, + JoinTable, + ManyToMany, + OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { Problem } from './problem.entity'; +import { Submission } from './submission.entity'; + +@Entity() +export class Competition { + @PrimaryGeneratedColumn() + @ApiProperty({ description: '대회 id' }) + id: number; + + @ApiProperty({ description: '대회를 개최한 유저의 id' }) + @Column() + userId: number; + + @ApiProperty({ description: '대회 이름' }) + @Column() + name: string; + + @ApiProperty({ description: '대회에 대한 설명글' }) + @Column('text') + detail: string; + + @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) + @Column() + maxParticipants: number; + + @ApiProperty({ description: '대회 시작 일시' }) + @Column() + startsAt: Date; + + @ApiProperty({ description: '대회 종료 일시' }) + @Column() + endsAt: Date; + + @ApiProperty({ description: '제출(submission) 테이블과 일대다 관계' }) + @OneToMany(() => Submission, (submission) => submission.competition) + submissions: Submission[]; + + @ApiProperty({ description: '문제(problem) 테이블과 다대다 관계' }) + @ManyToMany(() => Problem, (problem) => problem.competitions) + @JoinTable({ + name: 'CompetitionProblem', + joinColumn: { name: 'competitionId', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'problemId', referencedColumnName: 'id' }, + }) + problems: Problem[]; + + @ApiProperty({ description: '레코드 생성 일시' }) + @CreateDateColumn() + createdAt: Date; + + @ApiProperty({ description: '레코드 수정 일시' }) + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/be/algo-with-me-api/src/competition/entities/problem.entity.ts b/be/algo-with-me-api/src/competition/entities/problem.entity.ts index ebbcc93..e925634 100644 --- a/be/algo-with-me-api/src/competition/entities/problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/problem.entity.ts @@ -3,11 +3,14 @@ import { Column, CreateDateColumn, Entity, + ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, + JoinTable, } from 'typeorm'; +import { Competition } from './competition.entity'; import { Submission } from './submission.entity'; @Entity() @@ -44,6 +47,15 @@ export class Problem { @OneToMany(() => Submission, (submission) => submission.problem) submissions: Submission[]; + @ApiProperty() + @ManyToMany(() => Competition, (competition) => competition.problems) + @JoinTable({ + name: 'CompetitionProblem', + joinColumn: { name: 'problemId', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'competitionId', referencedColumnName: 'id' }, + }) + competitions: Competition[]; + @ApiProperty() @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts index 5f2596d..98a52ad 100644 --- a/be/algo-with-me-api/src/competition/entities/submission.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -7,6 +7,7 @@ import { UpdateDateColumn, } from 'typeorm'; +import { Competition } from './competition.entity'; import { Problem } from './problem.entity'; import { RESULT } from '../competition.enums'; @@ -31,6 +32,9 @@ export class Submission { @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) problem: Problem; + @ManyToOne(() => Competition, (competition) => competition.submissions) + competition: Competition; + @CreateDateColumn() createdAt: Date; From 89c7e65ff8259528c7743e870e77e6aa25bec1ba Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 18:40:00 +0900 Subject: [PATCH 049/226] =?UTF-8?q?feat:=20=EB=8C=80=ED=9A=8C=20CRU=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 44 ++++++++++++++++++- .../dto/competition.response.dto.ts | 36 +++++++++++++++ .../competition/dto/create-competition.dto.ts | 36 +++++++++++++++ .../competition/dto/update-competition.dto.ts | 36 +++++++++++++++ .../entities/competition.entity.ts | 4 -- .../services/competition.service.ts | 22 +++++++++- 6 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/dto/competition.response.dto.ts create mode 100644 be/algo-with-me-api/src/competition/dto/create-competition.dto.ts create mode 100644 be/algo-with-me-api/src/competition/dto/update-competition.dto.ts diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index a84c82d..2266af3 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,19 +1,59 @@ -import { Body, Controller, Get, Param, Post, UsePipes, ValidationPipe } from '@nestjs/common'; +import { + Body, + Controller, + Get, + Logger, + Param, + Post, + Put, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; +import { CreateCompetitionDto } from '../dto/create-competition.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; +import { UpdateCompetitionDto } from '../dto/update-competition.dto'; import { CompetitionService } from '../services/competition.service'; +import { CompetitionResponseDto } from '@src/competition/dto/competition.response.dto'; + @ApiTags('대회(competitions)') @Controller('competitions') export class CompetitionController { constructor(private readonly competitionService: CompetitionService) {} + @Get('/:id') + @ApiOperation({ summary: '대회 정보 조회' }) + @ApiResponse({ type: CompetitionResponseDto }) + async findOne(@Param('id') id: number) { + return await this.competitionService.findOne(id); + } + + @Post('/') + @ApiOperation({ summary: '대회 생성' }) + @ApiResponse({ type: CompetitionResponseDto }) + @UsePipes(new ValidationPipe({ transform: true })) + create(@Body() createCompetitionDto: CreateCompetitionDto) { + return this.competitionService.create(createCompetitionDto); + } + + @Put('/:id') + @ApiOperation({ + summary: '대회 정보 수정', + description: `URL의 파라미터(/:id)로 주어진 대회 id에 해당하는 대회 정보를 수정한다.`, + }) + @ApiResponse({}) + @UsePipes(new ValidationPipe({ transform: true })) + update(@Param('id') id: number, @Body() updateCompetitionDto: UpdateCompetitionDto) { + return this.competitionService.update(id, updateCompetitionDto); + } + @Get('problems/:id') @ApiOperation({ summary: '대회 문제 상세 조회' }) @ApiResponse({ type: CompetitionProblemResponseDto }) - findOne(@Param('id') id: number) { + findOneProblem(@Param('id') id: number) { return this.competitionService.findOneProblem(id); } diff --git a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts new file mode 100644 index 0000000..e778747 --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty } from 'class-validator'; + +export class CompetitionResponseDto { + @ApiProperty({ description: '대회 id' }) + @IsNotEmpty() + id: number; + + @ApiProperty({ description: '대회 이름' }) + @IsNotEmpty() + name: string; + + @ApiProperty({ description: '대회에 대한 설명글' }) + @IsNotEmpty() + detail: string; + + @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) + @IsNotEmpty() + maxParticipants: number; + + @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) + @IsNotEmpty() + startsAt: string; + + @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) + @IsNotEmpty() + endsAt: string; + + @ApiProperty({ description: '레코드 생성 일시 (ISO string)' }) + @IsNotEmpty() + createdAt: string; + + @ApiProperty({ description: '레코드 수정 일시 (ISO string)' }) + @IsNotEmpty() + updatedAt: string; +} diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts new file mode 100644 index 0000000..363bd37 --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty } from 'class-validator'; + +import { Competition } from '@src/competition/entities/competition.entity'; + +export class CreateCompetitionDto { + @ApiProperty({ description: '대회 이름' }) + @IsNotEmpty() + name: string; + + @ApiProperty({ description: '대회에 대한 설명글' }) + @IsNotEmpty() + detail: string; + + @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) + @IsNotEmpty() + maxParticipants: number; + + @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) + @IsNotEmpty() + startsAt: string; + + @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) + @IsNotEmpty() + endsAt: string; + + toEntity(): Competition { + const competition = new Competition(); + competition.name = this.name; + competition.detail = this.detail; + competition.maxParticipants = this.maxParticipants; + competition.startsAt = new Date(this.startsAt); + competition.endsAt = new Date(this.endsAt); + return competition; + } +} diff --git a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts new file mode 100644 index 0000000..0fb7d42 --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts @@ -0,0 +1,36 @@ +import { Optional } from '@nestjs/common'; +import { ApiProperty } from '@nestjs/swagger'; + +import { Competition } from '@src/competition/entities/competition.entity'; + +export class UpdateCompetitionDto { + @ApiProperty({ description: '대회 이름' }) + @Optional() + name: string; + + @ApiProperty({ description: '대회에 대한 설명글' }) + @Optional() + detail: string; + + @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) + @Optional() + maxParticipants: number; + + @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) + @Optional() + startsAt: string; + + @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) + @Optional() + endsAt: string; + + toEntity(): Competition { + const competition = new Competition(); + competition.name = this.name; + competition.detail = this.detail; + competition.maxParticipants = this.maxParticipants; + competition.startsAt = new Date(this.startsAt); + competition.endsAt = new Date(this.endsAt); + return competition; + } +} diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts index 5d28367..e514d9f 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -19,10 +19,6 @@ export class Competition { @ApiProperty({ description: '대회 id' }) id: number; - @ApiProperty({ description: '대회를 개최한 유저의 id' }) - @Column() - userId: number; - @ApiProperty({ description: '대회 이름' }) @Column() name: string; diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 82a41c6..d011d32 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,5 +1,5 @@ import { InjectQueue } from '@nestjs/bull'; -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; @@ -14,15 +14,33 @@ import { ScoreResultDto } from '../dto/score-result.dto'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; +import { CreateCompetitionDto } from '@src/competition/dto/create-competition.dto'; +import { UpdateCompetitionDto } from '@src/competition/dto/update-competition.dto'; +import { Competition } from '@src/competition/entities/competition.entity'; + @Injectable() export class CompetitionService { server: Server; constructor( + @InjectRepository(Competition) private readonly competitionRepository: Repository , @InjectRepository(Problem) private readonly problemRepository: Repository , @InjectRepository(Submission) private readonly submissionRepository: Repository , @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, ) {} + async findOne(id: number) { + return await this.competitionRepository.findOneBy({ id }); + } + + async create(createCompetitionDto: CreateCompetitionDto) { + return this.competitionRepository.create(createCompetitionDto.toEntity()); + } + + async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { + const result = await this.competitionRepository.update({ id: id }, { ...updateCompetitionDto }); + return !!result.affected; + } + async findOneProblem(id: number) { const problem = await this.problemRepository.findOneBy({ id }); const fileName = id.toString() + '.md'; @@ -55,8 +73,8 @@ export class CompetitionService { return savedSubmission; } - // TODO: 유저, 대회 도메인 구현 이후 수정 필요 + async saveScoreResult(scoreResultDto: ScoreResultDto) { const submission = await this.submissionRepository.findOneBy({ id: scoreResultDto.submissionId, From d7e3dea27c4e25ddf37186ecd205d33ca335f0d6 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 18:46:49 +0900 Subject: [PATCH 050/226] =?UTF-8?q?chore:=20swagger=EC=97=90=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EA=B0=88=20=EC=84=A4=EB=AA=85=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 2266af3..9c601ad 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -25,14 +25,20 @@ export class CompetitionController { constructor(private readonly competitionService: CompetitionService) {} @Get('/:id') - @ApiOperation({ summary: '대회 정보 조회' }) + @ApiOperation({ + summary: '대회 정보 조회', + description: 'URL의 파라미터(`/:id`)로 주어진 대회 id에 해당하는 대회 정보를 조회한다.', + }) @ApiResponse({ type: CompetitionResponseDto }) async findOne(@Param('id') id: number) { return await this.competitionService.findOne(id); } @Post('/') - @ApiOperation({ summary: '대회 생성' }) + @ApiOperation({ + summary: '대회 생성', + description: `주어진 대회 관련 정보를 이용해 대회를 생성한다.`, + }) @ApiResponse({ type: CompetitionResponseDto }) @UsePipes(new ValidationPipe({ transform: true })) create(@Body() createCompetitionDto: CreateCompetitionDto) { @@ -42,7 +48,7 @@ export class CompetitionController { @Put('/:id') @ApiOperation({ summary: '대회 정보 수정', - description: `URL의 파라미터(/:id)로 주어진 대회 id에 해당하는 대회 정보를 수정한다.`, + description: `URL의 파라미터(\`/:id\`)로 주어진 대회 id에 해당하는 대회 정보를 수정한다. request JSON 중 **수정하기를 원하는 것만** key: value 형식으로 요청한다.`, }) @ApiResponse({}) @UsePipes(new ValidationPipe({ transform: true })) @@ -59,8 +65,9 @@ export class CompetitionController { @Post('scores') @ApiOperation({ - summary: '채점 서버에서 채점 완료시 요청받을 api', - description: '채점 서버에서 테스트케이스 별로 채점이 완료될경우 호출할 api', + summary: '[백엔드 전용] 채점 서버에서 채점 완료시 요청받을 api', + description: + '**[프론트엔드에서는 사용되지 않음. 백엔드에서만 사용됨]** 채점 서버에서 테스트케이스 별로 채점이 완료될경우 호출할 api', }) @UsePipes(new ValidationPipe({ transform: true })) saveScoreResult(@Body() scoreResultDto: ScoreResultDto) { From 5892bfb44662d4ca1082f96f0664a49d3ffdf1ae Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 18:47:20 +0900 Subject: [PATCH 051/226] =?UTF-8?q?chore:=20eslint=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 12 +----------- .../src/competition/services/competition.service.ts | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 9c601ad..9f08b34 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,14 +1,4 @@ -import { - Body, - Controller, - Get, - Logger, - Param, - Post, - Put, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Put, UsePipes, ValidationPipe } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index d011d32..c86d246 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,5 +1,5 @@ import { InjectQueue } from '@nestjs/bull'; -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; From 913baddfa2316a9085e9bbeab2f3bbb53ad560e6 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Mon, 20 Nov 2023 18:56:44 +0900 Subject: [PATCH 052/226] =?UTF-8?q?chore:=20swagger=20Put=20response=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/controllers/competition.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 9f08b34..0901f7c 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -40,7 +40,7 @@ export class CompetitionController { summary: '대회 정보 수정', description: `URL의 파라미터(\`/:id\`)로 주어진 대회 id에 해당하는 대회 정보를 수정한다. request JSON 중 **수정하기를 원하는 것만** key: value 형식으로 요청한다.`, }) - @ApiResponse({}) + @ApiResponse({ type: Boolean }) @UsePipes(new ValidationPipe({ transform: true })) update(@Param('id') id: number, @Body() updateCompetitionDto: UpdateCompetitionDto) { return this.competitionService.update(id, updateCompetitionDto); From aebf487cfdfef79a70ab32687e74b78ddb119a46 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 21 Nov 2023 11:01:02 +0900 Subject: [PATCH 053/226] =?UTF-8?q?chore:=20api=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EB=8F=84=EC=BB=A4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/.dockerignore | 2 ++ be/algo-with-me-api/Dockerfile | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 be/algo-with-me-api/.dockerignore create mode 100644 be/algo-with-me-api/Dockerfile diff --git a/be/algo-with-me-api/.dockerignore b/be/algo-with-me-api/.dockerignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/be/algo-with-me-api/.dockerignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/be/algo-with-me-api/Dockerfile b/be/algo-with-me-api/Dockerfile new file mode 100644 index 0000000..5e2998d --- /dev/null +++ b/be/algo-with-me-api/Dockerfile @@ -0,0 +1,11 @@ +FROM node:20 + +WORKDIR /algo-with-me-api +COPY ./ ./ + +RUN npm install -g pnpm \ + && pnpm install + +EXPOSE 3000 + +CMD ["pnpm", "run", "start:dev"] From 44549ba2f4d8061a50963b7a44b80a794cb0965f Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:00:25 +0900 Subject: [PATCH 054/226] =?UTF-8?q?feat:=20github=20email=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/auth.controller.ts | 26 +++++++++++++ be/algo-with-me-api/src/auth/auth.module.ts | 8 +++- be/algo-with-me-api/src/auth/auth.strategy.ts | 37 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 be/algo-with-me-api/src/auth/auth.controller.ts create mode 100644 be/algo-with-me-api/src/auth/auth.strategy.ts diff --git a/be/algo-with-me-api/src/auth/auth.controller.ts b/be/algo-with-me-api/src/auth/auth.controller.ts new file mode 100644 index 0000000..d1a7f86 --- /dev/null +++ b/be/algo-with-me-api/src/auth/auth.controller.ts @@ -0,0 +1,26 @@ +import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Controller('auth') +export class AuthController { + constructor() {} + + @Get() + @UseGuards(AuthGuard('github')) + async login() { + // + } + + @Get('test') + @UseGuards(AuthGuard('github')) + async test() { + // + } + + @Get('callback') + @UseGuards(AuthGuard('github')) + async authCallback(@Req() req) { + // console.log(req); + return req.user; + } +} diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts index 7459c06..b359338 100644 --- a/be/algo-with-me-api/src/auth/auth.module.ts +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -1,4 +1,10 @@ import { Module } from '@nestjs/common'; -@Module({}) +import { AuthController } from './auth.controller'; +import { GithubStrategy } from './auth.strategy'; + +@Module({ + controllers: [AuthController], + providers: [GithubStrategy], +}) export class AuthModule {} diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts new file mode 100644 index 0000000..24d6ec7 --- /dev/null +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { PassportStrategy } from '@nestjs/passport'; +import { Profile, Strategy } from 'passport-github'; + +@Injectable() +export class GithubStrategy extends PassportStrategy(Strategy, 'github') { + constructor(configService: ConfigService) { + super({ + clientID: configService.get ('GITHUB_CLIENT_ID'), + clientSecret: configService.get ('GITHUB_CLIENT_SECRET'), + callbackURL: configService.get ('GITHUB_CALLBACK_URL'), + scope: ['public_profile', 'user:email'], + }); + } + + async validate(accessToken: string, _refreshToken: string, profile: Profile) { + profile.email = await this.getPrimaryEmail(accessToken); + return profile; + } + + async getPrimaryEmail(accessToken: string) { + const res: Response = await fetch('https://api.github.com/user/emails', { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + // github에 primary로 등록된 이메일을 가져온다. + const emails: object[] = await res.json(); + const email = emails.find((element) => { + if (element['primary']) return element; + }); + return email['email']; + } +} From 2d7677acd7937dbed3469b853273a2fb1cfb390a Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:46:50 +0900 Subject: [PATCH 055/226] =?UTF-8?q?fix:=20jwt=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=84=A4=EC=B9=98,=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 1 + be/algo-with-me-api/pnpm-lock.yaml | 88 +++++++++++++++++++ be/algo-with-me-api/src/app.module.ts | 5 ++ .../src/auth/auth.controller.ts | 17 ++-- be/algo-with-me-api/src/auth/auth.module.ts | 3 +- be/algo-with-me-api/src/auth/auth.service.ts | 21 +++++ be/algo-with-me-api/src/auth/auth.strategy.ts | 25 ++---- 7 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 be/algo-with-me-api/src/auth/auth.service.ts diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index 51d92e6..829fd27 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -24,6 +24,7 @@ "@nestjs/common": "^10.2.8", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", "@nestjs/mapped-types": "*", "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 65269b5..43cab04 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -17,6 +17,9 @@ dependencies: '@nestjs/core': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(@nestjs/websockets@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/jwt': + specifier: ^10.2.0 + version: 10.2.0(@nestjs/common@10.2.8) '@nestjs/mapped-types': specifier: '*' version: 2.0.3(@nestjs/common@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) @@ -1083,6 +1086,16 @@ packages: transitivePeerDependencies: - encoding + /@nestjs/jwt@10.2.0(@nestjs/common@10.2.8): + resolution: {integrity: sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@types/jsonwebtoken': 9.0.5 + jsonwebtoken: 9.0.2 + dev: false + /@nestjs/mapped-types@2.0.3(@nestjs/common@10.2.8)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): resolution: {integrity: sha512-40Zdqg98lqoF0+7ThWIZFStxgzisK6GG22+1ABO4kZiGF/Tu2FE+DYLw+Q9D94vcFWizJ+MSjNN4ns9r6hIGxw==} peerDependencies: @@ -1462,6 +1475,12 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/jsonwebtoken@9.0.5: + resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} + dependencies: + '@types/node': 20.9.0 + dev: false + /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true @@ -2207,6 +2226,10 @@ packages: node-int64: 0.4.0 dev: true + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -2761,6 +2784,12 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -4614,6 +4643,37 @@ packages: graceful-fs: 4.2.11 dev: true + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.5.4 + dev: false + + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: false + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -4668,10 +4728,34 @@ packages: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} dev: false + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + /lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} dev: false + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -4680,6 +4764,10 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 11f82a8..5bebb87 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -1,6 +1,7 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; +import { JwtModule } from '@nestjs/jwt'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; @@ -32,6 +33,10 @@ import { UserModule } from './user/user.module'; password: process.env.REDIS_PASSWORD, }, }), + JwtModule.register({ + signOptions: { expiresIn: process.env.JWT_ACCESSTOKEN_EXPIRE_TIME }, + secret: process.env.JWT_SECRET, + }), CompetitionModule, AuthModule, UserModule, diff --git a/be/algo-with-me-api/src/auth/auth.controller.ts b/be/algo-with-me-api/src/auth/auth.controller.ts index d1a7f86..dd877d9 100644 --- a/be/algo-with-me-api/src/auth/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/auth.controller.ts @@ -1,26 +1,19 @@ import { Controller, Get, Req, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; +import { ApiTags } from '@nestjs/swagger'; -@Controller('auth') +@ApiTags('인증(auths)') +@Controller('auths') export class AuthController { constructor() {} @Get() @UseGuards(AuthGuard('github')) - async login() { - // - } - - @Get('test') - @UseGuards(AuthGuard('github')) - async test() { - // - } + async login() {} - @Get('callback') + @Get('github/callback') @UseGuards(AuthGuard('github')) async authCallback(@Req() req) { - // console.log(req); return req.user; } } diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts index b359338..dc4ac76 100644 --- a/be/algo-with-me-api/src/auth/auth.module.ts +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; import { GithubStrategy } from './auth.strategy'; @Module({ controllers: [AuthController], - providers: [GithubStrategy], + providers: [GithubStrategy, AuthService], }) export class AuthModule {} diff --git a/be/algo-with-me-api/src/auth/auth.service.ts b/be/algo-with-me-api/src/auth/auth.service.ts new file mode 100644 index 0000000..9935e13 --- /dev/null +++ b/be/algo-with-me-api/src/auth/auth.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AuthService { + async getGithubPrimaryEmail(accessToken: string): Promise { + const res: Response = await fetch('https://api.github.com/user/emails', { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + // github에 primary로 등록된 이메일을 가져온다. + const emails: object[] = await res.json(); + console.log(emails); + const email = emails.find((element) => { + if (element['primary']) return element; + }); + return email['email']; + } +} diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts index 24d6ec7..3cd9b3f 100644 --- a/be/algo-with-me-api/src/auth/auth.strategy.ts +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -3,9 +3,14 @@ import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { Profile, Strategy } from 'passport-github'; +import { AuthService } from './auth.service'; + @Injectable() export class GithubStrategy extends PassportStrategy(Strategy, 'github') { - constructor(configService: ConfigService) { + constructor( + configService: ConfigService, + private readonly authService: AuthService, + ) { super({ clientID: configService.get ('GITHUB_CLIENT_ID'), clientSecret: configService.get ('GITHUB_CLIENT_SECRET'), @@ -15,23 +20,7 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') { } async validate(accessToken: string, _refreshToken: string, profile: Profile) { - profile.email = await this.getPrimaryEmail(accessToken); + profile.email = await this.authService.getGithubPrimaryEmail(accessToken); return profile; } - - async getPrimaryEmail(accessToken: string) { - const res: Response = await fetch('https://api.github.com/user/emails', { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); - - // github에 primary로 등록된 이메일을 가져온다. - const emails: object[] = await res.json(); - const email = emails.find((element) => { - if (element['primary']) return element; - }); - return email['email']; - } } From 2fe48438357396af0972153eabba0065203d6c59 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:52:26 +0900 Subject: [PATCH 056/226] =?UTF-8?q?feat:=20user=20entity=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 2 +- .../src/user/entities/user.entity.ts | 33 +++++++++++++++++++ be/algo-with-me-api/src/user/user.module.ts | 7 +++- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 be/algo-with-me-api/src/user/entities/user.entity.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 5bebb87..be7a173 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -24,7 +24,7 @@ import { UserModule } from './user/user.module'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem, Submission], + entities: [Problem, Submission, UserModule], }), BullModule.forRoot({ redis: { diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts new file mode 100644 index 0000000..799b762 --- /dev/null +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail } from 'class-validator'; +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +@Entity() +export class User { + @ApiProperty({ description: '유저 id' }) + @PrimaryGeneratedColumn() + id: number; + + @ApiProperty({ description: '이메일' }) + @Column() + @IsEmail() + email: string; + + @ApiProperty({ description: '닉네임 초기값은 이메일' }) + @Column() + nickname: string; + + @ApiProperty({ description: '생성일' }) + @CreateDateColumn() + createdAt: Date; + + @ApiProperty({ description: '수정일' }) + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/be/algo-with-me-api/src/user/user.module.ts b/be/algo-with-me-api/src/user/user.module.ts index 309e84a..634d867 100644 --- a/be/algo-with-me-api/src/user/user.module.ts +++ b/be/algo-with-me-api/src/user/user.module.ts @@ -1,4 +1,9 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -@Module({}) +import { User } from './entities/user.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([User])], +}) export class UserModule {} From 90b132abe8af12f54860471cb2467704b1fe32aa Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:21:07 +0900 Subject: [PATCH 057/226] =?UTF-8?q?feat:=20github=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C,=20=EC=9C=A0=EC=A0=80=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=97=86=EC=9C=BC=EB=A9=B4=20=EC=A0=80=EC=9E=A5=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=97=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 3 ++- be/algo-with-me-api/src/auth/auth.module.ts | 7 ++++-- be/algo-with-me-api/src/auth/auth.strategy.ts | 14 +++++++---- .../auth/{ => controllers}/auth.controller.ts | 0 .../src/auth/{ => services}/auth.service.ts | 3 ++- .../src/user/dto/user.response.dto.ts | 13 +++++++++++ .../src/user/services/user.service.ts | 23 +++++++++++++++++++ be/algo-with-me-api/src/user/user.module.ts | 3 +++ 8 files changed, 58 insertions(+), 8 deletions(-) rename be/algo-with-me-api/src/auth/{ => controllers}/auth.controller.ts (100%) rename be/algo-with-me-api/src/auth/{ => services}/auth.service.ts (95%) create mode 100644 be/algo-with-me-api/src/user/dto/user.response.dto.ts create mode 100644 be/algo-with-me-api/src/user/services/user.service.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index be7a173..d0a11ff 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -8,6 +8,7 @@ import { AuthModule } from './auth/auth.module'; import { CompetitionModule } from './competition/competition.module'; import { Problem } from './competition/entities/problem.entity'; import { Submission } from './competition/entities/submission.entity'; +import { User } from './user/entities/user.entity'; import { UserModule } from './user/user.module'; @Module({ @@ -24,7 +25,7 @@ import { UserModule } from './user/user.module'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem, Submission, UserModule], + entities: [Problem, Submission, UserModule, User], }), BullModule.forRoot({ redis: { diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts index dc4ac76..dcc6feb 100644 --- a/be/algo-with-me-api/src/auth/auth.module.ts +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -1,10 +1,13 @@ import { Module } from '@nestjs/common'; -import { AuthController } from './auth.controller'; -import { AuthService } from './auth.service'; import { GithubStrategy } from './auth.strategy'; +import { AuthController } from './controllers/auth.controller'; +import { AuthService } from './services/auth.service'; + +import { UserModule } from '@src/user/user.module'; @Module({ + imports: [UserModule], controllers: [AuthController], providers: [GithubStrategy, AuthService], }) diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts index 3cd9b3f..ab78b15 100644 --- a/be/algo-with-me-api/src/auth/auth.strategy.ts +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -3,13 +3,17 @@ import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { Profile, Strategy } from 'passport-github'; -import { AuthService } from './auth.service'; +import { AuthService } from './services/auth.service'; + +import { UserResponseDto } from '@src/user/dto/user.response.dto'; +import { UserService } from '@src/user/services/user.service'; @Injectable() export class GithubStrategy extends PassportStrategy(Strategy, 'github') { constructor( configService: ConfigService, private readonly authService: AuthService, + private readonly userService: UserService, ) { super({ clientID: configService.get ('GITHUB_CLIENT_ID'), @@ -19,8 +23,10 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') { }); } - async validate(accessToken: string, _refreshToken: string, profile: Profile) { - profile.email = await this.authService.getGithubPrimaryEmail(accessToken); - return profile; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async validate(accessToken: string, _refreshToken: string, _profile: Profile) { + const email: string = await this.authService.getGithubPrimaryEmail(accessToken); + const user: UserResponseDto = await this.userService.saveOrGetByEmail(email); + return user; } } diff --git a/be/algo-with-me-api/src/auth/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts similarity index 100% rename from be/algo-with-me-api/src/auth/auth.controller.ts rename to be/algo-with-me-api/src/auth/controllers/auth.controller.ts diff --git a/be/algo-with-me-api/src/auth/auth.service.ts b/be/algo-with-me-api/src/auth/services/auth.service.ts similarity index 95% rename from be/algo-with-me-api/src/auth/auth.service.ts rename to be/algo-with-me-api/src/auth/services/auth.service.ts index 9935e13..c88b7d4 100644 --- a/be/algo-with-me-api/src/auth/auth.service.ts +++ b/be/algo-with-me-api/src/auth/services/auth.service.ts @@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class AuthService { + constructor() {} + async getGithubPrimaryEmail(accessToken: string): Promise { const res: Response = await fetch('https://api.github.com/user/emails', { method: 'GET', @@ -12,7 +14,6 @@ export class AuthService { // github에 primary로 등록된 이메일을 가져온다. const emails: object[] = await res.json(); - console.log(emails); const email = emails.find((element) => { if (element['primary']) return element; }); diff --git a/be/algo-with-me-api/src/user/dto/user.response.dto.ts b/be/algo-with-me-api/src/user/dto/user.response.dto.ts new file mode 100644 index 0000000..5b3bd87 --- /dev/null +++ b/be/algo-with-me-api/src/user/dto/user.response.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UserResponseDto { + constructor(email: string, nickname: string) { + this.email = email; + this.nickname = nickname; + } + @ApiProperty({ description: '이메일' }) + email: string; + + @ApiProperty({ description: '닉네임' }) + nickname: string; +} diff --git a/be/algo-with-me-api/src/user/services/user.service.ts b/be/algo-with-me-api/src/user/services/user.service.ts new file mode 100644 index 0000000..80fa35f --- /dev/null +++ b/be/algo-with-me-api/src/user/services/user.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { UserResponseDto } from '../dto/user.response.dto'; +import { User } from '../entities/user.entity'; + +@Injectable() +export class UserService { + constructor(@InjectRepository(User) private readonly userRepository: Repository ) {} + + async saveOrGetByEmail(email: string) { + const user = await this.userRepository.findOneBy({ email }); + if (!user) { + const savedUser = await this.userRepository.save({ + email: email, + nickname: email, + }); + return new UserResponseDto(savedUser.email, savedUser.nickname); + } + return new UserResponseDto(user.email, user.nickname); + } +} diff --git a/be/algo-with-me-api/src/user/user.module.ts b/be/algo-with-me-api/src/user/user.module.ts index 634d867..a71b9d8 100644 --- a/be/algo-with-me-api/src/user/user.module.ts +++ b/be/algo-with-me-api/src/user/user.module.ts @@ -2,8 +2,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; +import { UserService } from './services/user.service'; @Module({ imports: [TypeOrmModule.forFeature([User])], + providers: [UserService], + exports: [UserService], }) export class UserModule {} From 5ac9d054c4783e196534c87709f34ba045033c9f Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:23:28 +0900 Subject: [PATCH 058/226] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=8B=9C=20jwt=20accessToken=20=EB=B0=98=ED=99=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/controllers/auth.controller.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts index dd877d9..388fb60 100644 --- a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts @@ -1,11 +1,12 @@ import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; import { AuthGuard } from '@nestjs/passport'; import { ApiTags } from '@nestjs/swagger'; @ApiTags('인증(auths)') @Controller('auths') export class AuthController { - constructor() {} + constructor(private jwtService: JwtService) {} @Get() @UseGuards(AuthGuard('github')) @@ -14,6 +15,10 @@ export class AuthController { @Get('github/callback') @UseGuards(AuthGuard('github')) async authCallback(@Req() req) { - return req.user; + const content = { + sub: req.user.email, + nickname: req.user.nickname, + }; + return { accessToken: this.jwtService.sign(content) }; } } From 503dd1da6e1c1b69cbb06f7f0f3ca3e173558844 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:02:18 +0900 Subject: [PATCH 059/226] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 1 + be/algo-with-me-api/pnpm-lock.yaml | 10 ++++++++++ be/algo-with-me-api/src/app.module.ts | 5 ----- be/algo-with-me-api/src/auth/auth.module.ts | 19 ++++++++++++++++--- be/algo-with-me-api/src/auth/auth.strategy.ts | 19 ++++++++++++++++++- .../src/auth/controllers/auth.controller.ts | 7 +++++++ 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index 829fd27..b5dfdf1 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -36,6 +36,7 @@ "class-validator": "^0.14.0", "passport": "^0.6.0", "passport-github": "^1.1.0", + "passport-jwt": "^4.0.1", "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 43cab04..7c6f66f 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -53,6 +53,9 @@ dependencies: passport-github: specifier: ^1.1.0 version: 1.1.0 + passport-jwt: + specifier: ^4.0.1 + version: 4.0.1 pg: specifier: ^8.11.3 version: 8.11.3 @@ -5282,6 +5285,13 @@ packages: passport-oauth2: 1.7.0 dev: false + /passport-jwt@4.0.1: + resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==} + dependencies: + jsonwebtoken: 9.0.2 + passport-strategy: 1.0.0 + dev: false + /passport-oauth2@1.7.0: resolution: {integrity: sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==} engines: {node: '>= 0.4.0'} diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index d0a11ff..2036dc4 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -1,7 +1,6 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { JwtModule } from '@nestjs/jwt'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; @@ -34,10 +33,6 @@ import { UserModule } from './user/user.module'; password: process.env.REDIS_PASSWORD, }, }), - JwtModule.register({ - signOptions: { expiresIn: process.env.JWT_ACCESSTOKEN_EXPIRE_TIME }, - secret: process.env.JWT_SECRET, - }), CompetitionModule, AuthModule, UserModule, diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts index dcc6feb..4bdc254 100644 --- a/be/algo-with-me-api/src/auth/auth.module.ts +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -1,14 +1,27 @@ import { Module } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { JwtModule } from '@nestjs/jwt'; -import { GithubStrategy } from './auth.strategy'; +import { GithubStrategy, JWTStrategy } from './auth.strategy'; import { AuthController } from './controllers/auth.controller'; import { AuthService } from './services/auth.service'; import { UserModule } from '@src/user/user.module'; @Module({ - imports: [UserModule], + imports: [ + UserModule, + JwtModule.registerAsync({ + useFactory: async (configService: ConfigService) => { + return { + signOptions: { expiresIn: configService.get ('JWT_ACCESSTOKEN_EXPIRE_TIME') }, + secret: configService.get ('JWT_SECRET'), + }; + }, + inject: [ConfigService], + }), + ], controllers: [AuthController], - providers: [GithubStrategy, AuthService], + providers: [GithubStrategy, AuthService, JWTStrategy], }) export class AuthModule {} diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts index ab78b15..621c79f 100644 --- a/be/algo-with-me-api/src/auth/auth.strategy.ts +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { Profile, Strategy } from 'passport-github'; +import { ExtractJwt, Strategy as PassportJwtStrategy } from 'passport-jwt'; import { AuthService } from './services/auth.service'; @@ -11,7 +12,7 @@ import { UserService } from '@src/user/services/user.service'; @Injectable() export class GithubStrategy extends PassportStrategy(Strategy, 'github') { constructor( - configService: ConfigService, + private readonly configService: ConfigService, private readonly authService: AuthService, private readonly userService: UserService, ) { @@ -30,3 +31,19 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') { return user; } } + +@Injectable() +export class JWTStrategy extends PassportStrategy(PassportJwtStrategy) { + constructor(private readonly configservice: ConfigService) { + console.log('test', configservice.get ('JWT_SECRET')); + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configservice.get ('JWT_SECRET'), + }); + } + + async validate(content: any) { + return { email: content.email, nickname: content.nickname }; + } +} diff --git a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts index 388fb60..a6ad53a 100644 --- a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts @@ -21,4 +21,11 @@ export class AuthController { }; return { accessToken: this.jwtService.sign(content) }; } + + // 인증 테스트 api + @Get('/tests') + @UseGuards(AuthGuard('jwt')) + test(@Req() req) { + return req.user; + } } From 8520d5e299cfe43d93b8717724a7d4377611a23e Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:16:13 +0900 Subject: [PATCH 060/226] =?UTF-8?q?chore:=20entities=20=EC=9D=B4=EC=83=81?= =?UTF-8?q?=ED=95=9C=EA=B1=B0=20=EC=B6=94=EA=B0=80=EB=90=9C=EA=B1=B0=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 2036dc4..34ae334 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -24,7 +24,7 @@ import { UserModule } from './user/user.module'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem, Submission, UserModule, User], + entities: [Problem, Submission, User], }), BullModule.forRoot({ redis: { From 55d69ee0d7d6dd8ed5a8dbe048851200d31caee6 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:30:34 +0900 Subject: [PATCH 061/226] =?UTF-8?q?fix:=20cors=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/main.ts b/be/algo-with-me-api/src/main.ts index b39f136..789c979 100644 --- a/be/algo-with-me-api/src/main.ts +++ b/be/algo-with-me-api/src/main.ts @@ -16,7 +16,7 @@ function setSwagger(app: INestApplication ) { } async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule, {cors: true}); setSwagger(app); await app.listen(3000); } From e6c4042ba913a2eb325e70fc8643c08399d697ad Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 21 Nov 2023 16:18:55 +0900 Subject: [PATCH 062/226] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=20api=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EA=B2=BD=EB=A1=9C=20=EA=B3=A0=EC=B9=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/services/competition.service.ts | 2 +- be/algo-with-me-api/src/competition/services/problem.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index c86d246..e0f3150 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -44,7 +44,7 @@ export class CompetitionService { async findOneProblem(id: number) { const problem = await this.problemRepository.findOneBy({ id }); const fileName = id.toString() + '.md'; - const paths = path.join(process.env.PROBLEM_PATH, id.toString(), fileName); + const paths = path.join(process.env.PROBLEM_PATH, fileName); if (!existsSync(paths)) throw new NotFoundException('문제 파일을 찾을 수 없습니다.'); const content = readFileSync(paths).toString(); return new CompetitionProblemResponseDto( diff --git a/be/algo-with-me-api/src/competition/services/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts index 30765a7..5cceabc 100644 --- a/be/algo-with-me-api/src/competition/services/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -31,7 +31,7 @@ export class ProblemService { async findOne(id: number) { const problem = await this.problemRepository.findOneBy({ id }); const fileName = id.toString() + '.md'; - const paths = path.join(process.env.PROBLEM_PATH, id.toString(), fileName); + const paths = path.join(process.env.PROBLEM_PATH, fileName); if (!existsSync(paths)) throw new NotFoundException('문제 파일을 찾을 수 없습니다.'); const content = readFileSync(paths).toString(); return new ProblemResponseDto( From d31d0c970eddf770aae1b452b1a7a19852ab54bd Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 21 Nov 2023 16:58:58 +0900 Subject: [PATCH 063/226] =?UTF-8?q?fix:=20competition=20find=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=83=81=ED=99=A9=20=EB=8C=80=EC=9D=91=20=EB=B0=8F?= =?UTF-8?q?=20create=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 4 ++-- .../dto/competition.response.dto.ts | 20 +++++++++++++++++++ .../competition/dto/create-competition.dto.ts | 14 +++++++++++++ .../competition/dto/update-competition.dto.ts | 14 +++++++++++++ .../services/competition.service.ts | 7 +++++-- 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 0901f7c..2582029 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -31,8 +31,8 @@ export class CompetitionController { }) @ApiResponse({ type: CompetitionResponseDto }) @UsePipes(new ValidationPipe({ transform: true })) - create(@Body() createCompetitionDto: CreateCompetitionDto) { - return this.competitionService.create(createCompetitionDto); + async create(@Body() createCompetitionDto: CreateCompetitionDto) { + return await this.competitionService.create(createCompetitionDto); } @Put('/:id') diff --git a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts index e778747..f60c7d0 100644 --- a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts @@ -2,6 +2,26 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; export class CompetitionResponseDto { + constructor( + id: number, + name: string, + detail: string, + maxParticipants: number, + startsAt: string, + endsAt: string, + createdAt: string, + updatedAt: string, + ) { + this.id = id; + this.name = name; + this.detail = detail; + this.maxParticipants = maxParticipants; + this.startsAt = startsAt; + this.endsAt = endsAt; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + @ApiProperty({ description: '대회 id' }) @IsNotEmpty() id: number; diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index 363bd37..e7a815b 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -4,6 +4,20 @@ import { IsNotEmpty } from 'class-validator'; import { Competition } from '@src/competition/entities/competition.entity'; export class CreateCompetitionDto { + constructor( + name: string, + detail: string, + maxParticipants: number, + startsAt: string, + endsAt: string, + ) { + this.name = name; + this.detail = detail; + this.maxParticipants = maxParticipants; + this.startsAt = startsAt; + this.endsAt = endsAt; + } + @ApiProperty({ description: '대회 이름' }) @IsNotEmpty() name: string; diff --git a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts index 0fb7d42..952f78d 100644 --- a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts @@ -4,6 +4,20 @@ import { ApiProperty } from '@nestjs/swagger'; import { Competition } from '@src/competition/entities/competition.entity'; export class UpdateCompetitionDto { + constructor( + name: string, + detail: string, + maxParticipants: number, + startsAt: string, + endsAt: string, + ) { + this.name = name; + this.detail = detail; + this.maxParticipants = maxParticipants; + this.startsAt = startsAt; + this.endsAt = endsAt; + } + @ApiProperty({ description: '대회 이름' }) @Optional() name: string; diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index e0f3150..61e1af3 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -29,11 +29,14 @@ export class CompetitionService { ) {} async findOne(id: number) { - return await this.competitionRepository.findOneBy({ id }); + const result = await this.competitionRepository.findOneBy({ id }); + if (result === null) + throw new NotFoundException(`대회 id ${id}에 해당하는 대회 정보를 찾을 수 없습니다`); + return result; } async create(createCompetitionDto: CreateCompetitionDto) { - return this.competitionRepository.create(createCompetitionDto.toEntity()); + return this.competitionRepository.save(createCompetitionDto.toEntity()); } async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From c06979557417fcf8d20350c8a4b706d7ac6ead88 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 21 Nov 2023 17:36:55 +0900 Subject: [PATCH 064/226] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 8 +++--- .../services/competition.service.ts | 26 ++++++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 2582029..617abee 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -20,8 +20,8 @@ export class CompetitionController { description: 'URL의 파라미터(`/:id`)로 주어진 대회 id에 해당하는 대회 정보를 조회한다.', }) @ApiResponse({ type: CompetitionResponseDto }) - async findOne(@Param('id') id: number) { - return await this.competitionService.findOne(id); + findOne(@Param('id') id: number) { + return this.competitionService.findOne(id); } @Post('/') @@ -31,8 +31,8 @@ export class CompetitionController { }) @ApiResponse({ type: CompetitionResponseDto }) @UsePipes(new ValidationPipe({ transform: true })) - async create(@Body() createCompetitionDto: CreateCompetitionDto) { - return await this.competitionService.create(createCompetitionDto); + create(@Body() createCompetitionDto: CreateCompetitionDto) { + return this.competitionService.create(createCompetitionDto); } @Put('/:id') diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 61e1af3..66c19bc 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -14,6 +14,7 @@ import { ScoreResultDto } from '../dto/score-result.dto'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; +import { CompetitionResponseDto } from '@src/competition/dto/competition.response.dto'; import { CreateCompetitionDto } from '@src/competition/dto/create-competition.dto'; import { UpdateCompetitionDto } from '@src/competition/dto/update-competition.dto'; import { Competition } from '@src/competition/entities/competition.entity'; @@ -30,13 +31,32 @@ export class CompetitionService { async findOne(id: number) { const result = await this.competitionRepository.findOneBy({ id }); - if (result === null) + if (!result) throw new NotFoundException(`대회 id ${id}에 해당하는 대회 정보를 찾을 수 없습니다`); - return result; + return new CompetitionResponseDto( + result.id, + result.name, + result.detail, + result.maxParticipants, + result.startsAt.toISOString(), + result.endsAt.toISOString(), + result.createdAt.toISOString(), + result.updatedAt.toISOString(), + ); } async create(createCompetitionDto: CreateCompetitionDto) { - return this.competitionRepository.save(createCompetitionDto.toEntity()); + const result = await this.competitionRepository.save(createCompetitionDto.toEntity()); + return new CompetitionResponseDto( + result.id, + result.name, + result.detail, + result.maxParticipants, + result.startsAt.toISOString(), + result.endsAt.toISOString(), + result.createdAt.toISOString(), + result.updatedAt.toISOString(), + ); } async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From d0796245380e3cc405e73fe2be752d851b76dbfb Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:20:46 +0900 Subject: [PATCH 065/226] =?UTF-8?q?fix:=20api=20=EC=84=A4=EB=AA=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20validate=EC=97=90=EC=84=9C=20email=20?= =?UTF-8?q?=EC=9E=98=EB=AA=BB=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8D=98?= =?UTF-8?q?=EA=B2=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/auth/auth.strategy.ts | 3 +-- .../src/auth/controllers/auth.controller.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts index 621c79f..8309036 100644 --- a/be/algo-with-me-api/src/auth/auth.strategy.ts +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -35,7 +35,6 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') { @Injectable() export class JWTStrategy extends PassportStrategy(PassportJwtStrategy) { constructor(private readonly configservice: ConfigService) { - console.log('test', configservice.get ('JWT_SECRET')); super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, @@ -44,6 +43,6 @@ export class JWTStrategy extends PassportStrategy(PassportJwtStrategy) { } async validate(content: any) { - return { email: content.email, nickname: content.nickname }; + return { email: content.sub, nickname: content.nickname }; } } diff --git a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts index a6ad53a..7f0cc76 100644 --- a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts @@ -1,19 +1,27 @@ import { Controller, Get, Req, UseGuards } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { AuthGuard } from '@nestjs/passport'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; @ApiTags('인증(auths)') @Controller('auths') export class AuthController { constructor(private jwtService: JwtService) {} - @Get() + @Get('github') + @ApiOperation({ + summary: 'github 로그인/회원가입', + description: 'github 로그인/회원가입 api 입니다.', + }) @UseGuards(AuthGuard('github')) async login() {} @Get('github/callback') @UseGuards(AuthGuard('github')) + @ApiOperation({ + summary: '깃허브 인증 완료시 리다이렉트 되는 api', + description: '깃허브 인증이 완료되면 리다이렉트 되는 api 입니다. accessToken을 반환합니다.', + }) async authCallback(@Req() req) { const content = { sub: req.user.email, From 78f1323dc51f8dd54bddeb9ba2d18fce8699ff3e Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:54:53 +0900 Subject: [PATCH 066/226] =?UTF-8?q?fix:=20=EB=8B=A4=EB=8C=80=EB=8B=A4=20?= =?UTF-8?q?=EB=8D=B0=EC=BD=94=EB=A0=88=EC=9D=B4=ED=84=B0=EB=A5=BC=20=20?= =?UTF-8?q?=EC=9D=BC=EB=8C=80=EB=8B=A4=20=EB=8B=A4=EB=8C=80=EC=9D=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 4 +++- .../src/competition/competition.module.ts | 3 ++- .../entities/competition.entity.ts | 15 ++++-------- .../entities/competition.problem.entity.ts | 23 +++++++++++++++++++ .../competition/entities/problem.entity.ts | 15 ++++-------- 5 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index f574bfc..0298e2f 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -5,6 +5,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; import { CompetitionModule } from './competition/competition.module'; +import { CompetitionProblem } from './competition/entities/competition.problem.entity'; import { Problem } from './competition/entities/problem.entity'; import { Submission } from './competition/entities/submission.entity'; import { User } from './user/entities/user.entity'; @@ -26,7 +27,8 @@ import { Competition } from '@src/competition/entities/competition.entity'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem, Submission, Competition, User], + entities: [Problem, Submission, Competition, User, CompetitionProblem], + logging: true, }), BullModule.forRoot({ redis: { diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 6de3de7..9d91a53 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CompetitionController } from './controllers/competition.controller'; import { ProblemController } from './controllers/problem.controller'; +import { CompetitionProblem } from './entities/competition.problem.entity'; import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; import { CompetitionGateWay } from './gateways/competition.gateway'; @@ -15,7 +16,7 @@ import { Competition } from '@src/competition/entities/competition.entity'; @Module({ imports: [ - TypeOrmModule.forFeature([Problem, Submission, Competition]), + TypeOrmModule.forFeature([Problem, Submission, Competition, CompetitionProblem]), BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts index e514d9f..c9cd73f 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -3,14 +3,12 @@ import { Column, CreateDateColumn, Entity, - JoinTable, - ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import { Problem } from './problem.entity'; +import { CompetitionProblem } from './competition.problem.entity'; import { Submission } from './submission.entity'; @Entity() @@ -43,14 +41,9 @@ export class Competition { @OneToMany(() => Submission, (submission) => submission.competition) submissions: Submission[]; - @ApiProperty({ description: '문제(problem) 테이블과 다대다 관계' }) - @ManyToMany(() => Problem, (problem) => problem.competitions) - @JoinTable({ - name: 'CompetitionProblem', - joinColumn: { name: 'competitionId', referencedColumnName: 'id' }, - inverseJoinColumn: { name: 'problemId', referencedColumnName: 'id' }, - }) - problems: Problem[]; + @ApiProperty({ description: '대회문제(competitionProblem) 테이블과 일대다 관계' }) + @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) + competitionProblems: CompetitionProblem[]; @ApiProperty({ description: '레코드 생성 일시' }) @CreateDateColumn() diff --git a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts new file mode 100644 index 0000000..498b2cd --- /dev/null +++ b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; + +import { Competition } from './competition.entity'; +import { Problem } from './problem.entity'; + +@Entity() +export class CompetitionProblem { + @PrimaryGeneratedColumn() + @ApiProperty({ description: 'id' }) + id: number; + + @ApiProperty({ description: '대회(competition) 테이블과 다대일 관계' }) + @ManyToOne(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) + competition: Competition; + + @ApiProperty({ description: '문제(problem) 테이블과 다대일 관계' }) + @ManyToOne(() => CompetitionProblem, (CompetitionProblem) => CompetitionProblem.problem) + problem: Problem; + + @CreateDateColumn() + createdAt: Date; +} diff --git a/be/algo-with-me-api/src/competition/entities/problem.entity.ts b/be/algo-with-me-api/src/competition/entities/problem.entity.ts index e925634..06ae38d 100644 --- a/be/algo-with-me-api/src/competition/entities/problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/problem.entity.ts @@ -3,14 +3,12 @@ import { Column, CreateDateColumn, Entity, - ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, - JoinTable, } from 'typeorm'; -import { Competition } from './competition.entity'; +import { CompetitionProblem } from './competition.problem.entity'; import { Submission } from './submission.entity'; @Entity() @@ -47,14 +45,9 @@ export class Problem { @OneToMany(() => Submission, (submission) => submission.problem) submissions: Submission[]; - @ApiProperty() - @ManyToMany(() => Competition, (competition) => competition.problems) - @JoinTable({ - name: 'CompetitionProblem', - joinColumn: { name: 'problemId', referencedColumnName: 'id' }, - inverseJoinColumn: { name: 'competitionId', referencedColumnName: 'id' }, - }) - competitions: Competition[]; + @ApiProperty({ description: '대회문제(competitionProblem) 테이블과 일대다 관계' }) + @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.problem) + competitionProblems: CompetitionProblem[]; @ApiProperty() @CreateDateColumn() From 8d0f1dafa6a9631de73bf41f084b580db1453823 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:24:13 +0900 Subject: [PATCH 067/226] =?UTF-8?q?feat:=20=EB=8C=80=ED=9A=8C=EB=B3=84=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EB=AA=A9=EB=A1=9D=20=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 8 +++++ .../controllers/problem.controller.ts | 4 +-- ....dto.ts => problem.simple.response.dto.ts} | 2 +- .../entities/competition.problem.entity.ts | 4 +-- .../services/competition.service.ts | 29 ++++++++++++++++++- .../competition/services/problem.service.ts | 4 +-- 6 files changed, 43 insertions(+), 8 deletions(-) rename be/algo-with-me-api/src/competition/dto/{problem.list.response.dto.ts => problem.simple.response.dto.ts} (83%) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 617abee..bc5457c 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -3,6 +3,7 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; import { CreateCompetitionDto } from '../dto/create-competition.dto'; +import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; import { UpdateCompetitionDto } from '../dto/update-competition.dto'; import { CompetitionService } from '../services/competition.service'; @@ -46,6 +47,13 @@ export class CompetitionController { return this.competitionService.update(id, updateCompetitionDto); } + @Get('/:competitionId/problems') + @ApiOperation({ summary: '대회별 문제 목록 조회' }) + @ApiResponse({ type: ProblemSimpleResponseDto, isArray: true }) + findCompetitionProblem(@Param('competitionId') competitionId: number) { + return this.competitionService.findCompetitionProblemList(competitionId); + } + @Get('problems/:id') @ApiOperation({ summary: '대회 문제 상세 조회' }) @ApiResponse({ type: CompetitionProblemResponseDto }) diff --git a/be/algo-with-me-api/src/competition/controllers/problem.controller.ts b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts index d73c533..ef5dd42 100644 --- a/be/algo-with-me-api/src/competition/controllers/problem.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/problem.controller.ts @@ -11,8 +11,8 @@ import { import { ApiCreatedResponse, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CreateProblemDto } from '../dto/create-problem.dto'; -import { ProblemListResponseDto } from '../dto/problem.list.response.dto'; import { ProblemResponseDto } from '../dto/problem.response.dto'; +import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { Problem } from '../entities/problem.entity'; import { ProblemService } from '../services/problem.service'; @@ -31,7 +31,7 @@ export class ProblemController { @Get() @ApiOperation({ summary: '문제 전체 조회', description: '문제 목록을 조회한다.' }) - @ApiResponse({ type: ProblemListResponseDto, isArray: true }) + @ApiResponse({ type: ProblemSimpleResponseDto, isArray: true }) findAll() { return this.problemService.findAll(); } diff --git a/be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts b/be/algo-with-me-api/src/competition/dto/problem.simple.response.dto.ts similarity index 83% rename from be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts rename to be/algo-with-me-api/src/competition/dto/problem.simple.response.dto.ts index 8698796..9cea73e 100644 --- a/be/algo-with-me-api/src/competition/dto/problem.list.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/problem.simple.response.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class ProblemListResponseDto { +export class ProblemSimpleResponseDto { constructor(id: number, title: string) { this.id = id; this.title = title; diff --git a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts index 498b2cd..95cbb8b 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts @@ -11,11 +11,11 @@ export class CompetitionProblem { id: number; @ApiProperty({ description: '대회(competition) 테이블과 다대일 관계' }) - @ManyToOne(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) + @ManyToOne(() => Competition, (competition) => competition.competitionProblems) competition: Competition; @ApiProperty({ description: '문제(problem) 테이블과 다대일 관계' }) - @ManyToOne(() => CompetitionProblem, (CompetitionProblem) => CompetitionProblem.problem) + @ManyToOne(() => Problem, (problem) => problem.competitionProblems) problem: Problem; @CreateDateColumn() diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 66c19bc..5941841 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -10,7 +10,9 @@ import * as path from 'path'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; +import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; +import { CompetitionProblem } from '../entities/competition.problem.entity'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; @@ -26,6 +28,8 @@ export class CompetitionService { @InjectRepository(Competition) private readonly competitionRepository: Repository , @InjectRepository(Problem) private readonly problemRepository: Repository , @InjectRepository(Submission) private readonly submissionRepository: Repository , + @InjectRepository(CompetitionProblem) + private readonly competitionProblemRepository: Repository , @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, ) {} @@ -96,8 +100,8 @@ export class CompetitionService { return savedSubmission; } - // TODO: 유저, 대회 도메인 구현 이후 수정 필요 + // TODO: 유저, 대회 도메인 구현 이후 수정 필요 async saveScoreResult(scoreResultDto: ScoreResultDto) { const submission = await this.submissionRepository.findOneBy({ id: scoreResultDto.submissionId, @@ -116,4 +120,27 @@ export class CompetitionService { result['stdOut'] = scoreResultDto.stdOut; this.server.to(scoreResultDto.socketId).emit('scoreResult', result); } + + async findCompetitionProblemList(competitionId: number) { + const competition = await this.competitionProblemRepository.find({ + select: { + problem: { + id: true, + title: true, + }, + }, + where: { + competition: { + id: competitionId, + }, + }, + relations: { + problem: true, + }, + }); + + return competition.map((element: CompetitionProblem) => { + return new ProblemSimpleResponseDto(element.problem.id, element.problem.title); + }); + } } diff --git a/be/algo-with-me-api/src/competition/services/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts index 5cceabc..f47376a 100644 --- a/be/algo-with-me-api/src/competition/services/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -6,8 +6,8 @@ import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; import { CreateProblemDto } from '../dto/create-problem.dto'; -import { ProblemListResponseDto } from '../dto/problem.list.response.dto'; import { ProblemResponseDto } from '../dto/problem.response.dto'; +import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { Problem } from '../entities/problem.entity'; @Injectable() @@ -24,7 +24,7 @@ export class ProblemService { async findAll() { const problems = await this.problemRepository.find(); return problems.map((problem: Problem) => { - return new ProblemListResponseDto(problem.id, problem.title); + return new ProblemSimpleResponseDto(problem.id, problem.title); }); } From 359a4197cbe6304322f1aa84fdfae6c7e9216a81 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:26:38 +0900 Subject: [PATCH 068/226] =?UTF-8?q?fix:=20controller=EC=97=90=EC=84=9C=20i?= =?UTF-8?q?d=EA=B0=80=20=EB=AC=B4=EC=8A=A8=20id=EC=9D=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8C=8C=EC=95=85=ED=95=98=EA=B8=B0=20=EC=89=BD=EA=B2=8C=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index bc5457c..74f3ac9 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -15,14 +15,14 @@ import { CompetitionResponseDto } from '@src/competition/dto/competition.respons export class CompetitionController { constructor(private readonly competitionService: CompetitionService) {} - @Get('/:id') + @Get('/:competitionId') @ApiOperation({ summary: '대회 정보 조회', description: 'URL의 파라미터(`/:id`)로 주어진 대회 id에 해당하는 대회 정보를 조회한다.', }) @ApiResponse({ type: CompetitionResponseDto }) - findOne(@Param('id') id: number) { - return this.competitionService.findOne(id); + findOne(@Param('competitionId') competitionId: number) { + return this.competitionService.findOne(competitionId); } @Post('/') @@ -36,15 +36,18 @@ export class CompetitionController { return this.competitionService.create(createCompetitionDto); } - @Put('/:id') + @Put('/:competitionId') @ApiOperation({ summary: '대회 정보 수정', description: `URL의 파라미터(\`/:id\`)로 주어진 대회 id에 해당하는 대회 정보를 수정한다. request JSON 중 **수정하기를 원하는 것만** key: value 형식으로 요청한다.`, }) @ApiResponse({ type: Boolean }) @UsePipes(new ValidationPipe({ transform: true })) - update(@Param('id') id: number, @Body() updateCompetitionDto: UpdateCompetitionDto) { - return this.competitionService.update(id, updateCompetitionDto); + update( + @Param('competitionId') competitionId: number, + @Body() updateCompetitionDto: UpdateCompetitionDto, + ) { + return this.competitionService.update(competitionId, updateCompetitionDto); } @Get('/:competitionId/problems') @@ -54,11 +57,11 @@ export class CompetitionController { return this.competitionService.findCompetitionProblemList(competitionId); } - @Get('problems/:id') + @Get('problems/:problemId') @ApiOperation({ summary: '대회 문제 상세 조회' }) @ApiResponse({ type: CompetitionProblemResponseDto }) - findOneProblem(@Param('id') id: number) { - return this.competitionService.findOneProblem(id); + findOneProblem(@Param('problemId') problemId: number) { + return this.competitionService.findOneProblem(problemId); } @Post('scores') From 0749fe3d46c9e2bc2312abb412c3ba14d3bca552 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 22 Nov 2023 18:21:23 +0900 Subject: [PATCH 069/226] =?UTF-8?q?fix:=20=EA=B9=83=ED=97=88=EB=B8=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=99=84=EB=A3=8C=EC=8B=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=A0=ED=8A=B8=20url=EB=A1=9C=20=EB=A6=AC=EB=94=94?= =?UTF-8?q?=EB=A0=89=EC=85=98=20=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/controllers/auth.controller.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts index 7f0cc76..ae8b126 100644 --- a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { Controller, Get, Redirect, Req, UseGuards } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { AuthGuard } from '@nestjs/passport'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; @@ -18,6 +18,7 @@ export class AuthController { @Get('github/callback') @UseGuards(AuthGuard('github')) + @Redirect() @ApiOperation({ summary: '깃허브 인증 완료시 리다이렉트 되는 api', description: '깃허브 인증이 완료되면 리다이렉트 되는 api 입니다. accessToken을 반환합니다.', @@ -27,7 +28,11 @@ export class AuthController { sub: req.user.email, nickname: req.user.nickname, }; - return { accessToken: this.jwtService.sign(content) }; + return { + url: `https://boostcampwm2023.github.io/web12-algo-with-me?accessToken=${this.jwtService.sign( + content, + )}`, + }; } // 인증 테스트 api From 4e7f5da38dc17402cae27b808106db6a5adde1b4 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 22 Nov 2023 21:39:27 +0900 Subject: [PATCH 070/226] =?UTF-8?q?fix:=20=EC=9C=A0=EC=A0=80-=EC=A0=9C?= =?UTF-8?q?=EC=B6=9C=20=EA=B4=80=EA=B3=84,=20not=20null=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/entities/submission.entity.ts | 7 ++++++- be/algo-with-me-api/src/user/entities/user.entity.ts | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts index 98a52ad..b9eb73e 100644 --- a/be/algo-with-me-api/src/competition/entities/submission.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -11,6 +11,8 @@ import { Competition } from './competition.entity'; import { Problem } from './problem.entity'; import { RESULT } from '../competition.enums'; +import { User } from '@src/user/entities/user.entity'; + @Entity() export class Submission { @PrimaryGeneratedColumn() @@ -32,9 +34,12 @@ export class Submission { @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) problem: Problem; - @ManyToOne(() => Competition, (competition) => competition.submissions) + @ManyToOne(() => Competition, (competition) => competition.submissions, { nullable: false }) competition: Competition; + @ManyToOne(() => User, (user) => user.submissions, { nullable: false }) + user: User; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index 799b762..4e39e3a 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -4,10 +4,13 @@ import { Column, CreateDateColumn, Entity, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { Submission } from '@src/competition/entities/submission.entity'; + @Entity() export class User { @ApiProperty({ description: '유저 id' }) @@ -23,6 +26,10 @@ export class User { @Column() nickname: string; + @ApiProperty({ description: '제출(submission) 테이블과 일대다 관계' }) + @OneToMany(() => Submission, (submission) => submission.user, { nullable: false }) + submissions: Submission; + @ApiProperty({ description: '생성일' }) @CreateDateColumn() createdAt: Date; From 733349a66c1c412facfed5e71b4499fc15a0c0af Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:21:00 +0900 Subject: [PATCH 071/226] =?UTF-8?q?fix:=20=EC=9E=84=EC=8B=9C=20=EC=BB=A8?= =?UTF-8?q?=EC=8A=88=EB=A8=B8=20=EC=A3=BC=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/competition.module.ts | 3 +- .../src/competition/tem.consumer.ts | 40 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 9d91a53..34eb299 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -10,7 +10,6 @@ import { Submission } from './entities/submission.entity'; import { CompetitionGateWay } from './gateways/competition.gateway'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; -import { SubmissionConsumer } from './tem.consumer'; import { Competition } from '@src/competition/entities/competition.entity'; @@ -22,6 +21,6 @@ import { Competition } from '@src/competition/entities/competition.entity'; }), ], controllers: [ProblemController, CompetitionController], - providers: [ProblemService, CompetitionService, SubmissionConsumer, CompetitionGateWay], + providers: [ProblemService, CompetitionService, CompetitionGateWay], }) export class CompetitionModule {} diff --git a/be/algo-with-me-api/src/competition/tem.consumer.ts b/be/algo-with-me-api/src/competition/tem.consumer.ts index 50ba961..5fe09d1 100644 --- a/be/algo-with-me-api/src/competition/tem.consumer.ts +++ b/be/algo-with-me-api/src/competition/tem.consumer.ts @@ -1,22 +1,22 @@ -import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; -import { Job } from 'bull'; +// import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +// import { Job } from 'bull'; -@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) -export class SubmissionConsumer { - @Process() - async transcode(job: Job ) { - console.log(job.data); - for (let i = 0; i < 10; i++) { - console.log(i); - } - return { good: 'good' }; - } +// @Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +// export class SubmissionConsumer { +// @Process() +// async transcode(job: Job ) { +// console.log(job.data); +// for (let i = 0; i < 10; i++) { +// console.log(i); +// } +// return { good: 'good' }; +// } - @OnQueueCompleted() - onCompleted(job: Job , result: any) { - console.log('job done'); - console.log(result); - // redis 에서 데이터 삭제 - job.remove(); - } -} +// @OnQueueCompleted() +// onCompleted(job: Job , result: any) { +// console.log('job done'); +// console.log(result); +// // redis 에서 데이터 삭제 +// job.remove(); +// } +// } From 49d1ac80138fa18952140f023e637354228acd80 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 13:31:09 +0900 Subject: [PATCH 072/226] =?UTF-8?q?feat:=20websocket=20=EC=97=B0=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=ED=99=95=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/auth/auth.module.ts | 1 + .../src/auth/services/auth.service.ts | 16 ++++++++++++++-- .../src/competition/competition.module.ts | 2 ++ .../competition/gateways/competition.gateway.ts | 8 +++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts index 4bdc254..d9dc03e 100644 --- a/be/algo-with-me-api/src/auth/auth.module.ts +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -23,5 +23,6 @@ import { UserModule } from '@src/user/user.module'; ], controllers: [AuthController], providers: [GithubStrategy, AuthService, JWTStrategy], + exports: [AuthService], }) export class AuthModule {} diff --git a/be/algo-with-me-api/src/auth/services/auth.service.ts b/be/algo-with-me-api/src/auth/services/auth.service.ts index c88b7d4..a270084 100644 --- a/be/algo-with-me-api/src/auth/services/auth.service.ts +++ b/be/algo-with-me-api/src/auth/services/auth.service.ts @@ -1,8 +1,9 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; @Injectable() export class AuthService { - constructor() {} + constructor(private readonly jwtService: JwtService) {} async getGithubPrimaryEmail(accessToken: string): Promise { const res: Response = await fetch('https://api.github.com/user/emails', { @@ -19,4 +20,15 @@ export class AuthService { }); return email['email']; } + + verifyToken(token: string) { + if (!token) throw new UnauthorizedException('토큰을 찾을 수 없습니다.'); + const tokens = token.split(' '); + if (tokens.length !== 2) throw new UnauthorizedException('토큰의 양식이 올바르지 않습니다.'); + try { + return this.jwtService.verify(tokens[1]); + } catch { + throw new UnauthorizedException('유효하지 않은 토큰입니다.'); + } + } } diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 34eb299..c344165 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -11,6 +11,7 @@ import { CompetitionGateWay } from './gateways/competition.gateway'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; +import { AuthModule } from '@src/auth/auth.module'; import { Competition } from '@src/competition/entities/competition.entity'; @Module({ @@ -19,6 +20,7 @@ import { Competition } from '@src/competition/entities/competition.entity'; BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), + AuthModule, ], controllers: [ProblemController, CompetitionController], providers: [ProblemService, CompetitionService, CompetitionGateWay], diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 0229cab..81ffbf1 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -14,12 +14,17 @@ import { Server, Socket } from 'socket.io'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { CompetitionService } from '../services/competition.service'; +import { AuthService } from '@src/auth/services/auth.service'; + @WebSocketGateway({ namespace: 'competitions' }) export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @WebSocketServer() server: Server; - constructor(private readonly competitionService: CompetitionService) {} + constructor( + private readonly competitionService: CompetitionService, + private readonly authService: AuthService, + ) {} afterInit(server: Server) { this.competitionService.server = server; @@ -54,6 +59,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { public handleConnection(client: Socket, ...args: any[]) { // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 const { competitionId } = client.handshake.query; + this.authService.verifyToken(client.handshake.headers.authorization); client.join(competitionId); console.log(client.id); console.log(competitionId, args); From 1b7d62a1b1eb9abf9fef0e9dff64b8ff47f91fc8 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 13:59:59 +0900 Subject: [PATCH 073/226] =?UTF-8?q?feat:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9D=B8=EC=A6=9D=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateways/competition.gateway.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 81ffbf1..f97a77b 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,5 +1,6 @@ -import { UsePipes, ValidationPipe } from '@nestjs/common'; +import { UseFilters, UsePipes, ValidationPipe } from '@nestjs/common'; import { + BaseWsExceptionFilter, ConnectedSocket, MessageBody, OnGatewayConnection, @@ -57,11 +58,15 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { } public handleConnection(client: Socket, ...args: any[]) { - // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 - const { competitionId } = client.handshake.query; - this.authService.verifyToken(client.handshake.headers.authorization); - client.join(competitionId); - console.log(client.id); - console.log(competitionId, args); + try { + const { competitionId } = client.handshake.query; + this.authService.verifyToken(client.handshake.headers.authorization); + // TODO: 유저가 대회 참가했는지 검증 필요 + client.join(competitionId); + console.log(client.id); + console.log(competitionId, args); + } catch (error) { + client.emit('messages', { message: `${error.message}` }); + } } } From 1b9507409468c532c1dc8e5c32cd675e05571c5e Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 22 Nov 2023 18:00:26 +0900 Subject: [PATCH 074/226] =?UTF-8?q?feat:=20=EB=8C=80=ED=9A=8C=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 14 ++++- .../dto/competition.simple-response.dto.ts | 58 +++++++++++++++++++ .../services/competition.service.ts | 17 ++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 74f3ac9..bf6c230 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -15,10 +15,20 @@ import { CompetitionResponseDto } from '@src/competition/dto/competition.respons export class CompetitionController { constructor(private readonly competitionService: CompetitionService) {} + @Get('/') + @ApiOperation({ + summary: '대회 정보 전체 조회', + description: '모든 대회 정보를 조회한다.', + }) + @ApiResponse({ type: CompetitionResponseDto }) + findAll() { + return this.competitionService.findAll(); + } + @Get('/:competitionId') @ApiOperation({ - summary: '대회 정보 조회', - description: 'URL의 파라미터(`/:id`)로 주어진 대회 id에 해당하는 대회 정보를 조회한다.', + summary: '대회 세부 정보 조회', + description: 'URL의 파라미터(`/:id`)로 주어진 대회 id에 해당하는 대회 세부 정보를 조회한다.', }) @ApiResponse({ type: CompetitionResponseDto }) findOne(@Param('competitionId') competitionId: number) { diff --git a/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts new file mode 100644 index 0000000..dc2fb50 --- /dev/null +++ b/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts @@ -0,0 +1,58 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty } from 'class-validator'; + +import { Competition } from '@src/competition/entities/competition.entity'; + +export class CompetitionSimpleResponseDto { + constructor( + id: number, + name: string, + detail: string, + maxParticipants: number, + startsAt: string, + endsAt: string, + createdAt: string, + updatedAt: string, + ) { + this.id = id; + this.name = name; + this.detail = detail; + this.maxParticipants = maxParticipants; + this.startsAt = startsAt; + this.endsAt = endsAt; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + @ApiProperty({ description: '대회 id' }) + @IsNotEmpty() + id: number; + + @ApiProperty({ description: '대회 이름' }) + @IsNotEmpty() + name: string; + + @ApiProperty({ description: '대회에 대한 설명글' }) + @IsNotEmpty() + detail: string; + + @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) + @IsNotEmpty() + maxParticipants: number; + + @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) + @IsNotEmpty() + startsAt: string; + + @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) + @IsNotEmpty() + endsAt: string; + + @ApiProperty({ description: '레코드 생성 일시 (ISO string)' }) + @IsNotEmpty() + createdAt: string; + + @ApiProperty({ description: '레코드 수정 일시 (ISO string)' }) + @IsNotEmpty() + updatedAt: string; +} diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 5941841..ce9957a 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -17,6 +17,7 @@ import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; import { CompetitionResponseDto } from '@src/competition/dto/competition.response.dto'; +import { CompetitionSimpleResponseDto } from '@src/competition/dto/competition.simple-response.dto'; import { CreateCompetitionDto } from '@src/competition/dto/create-competition.dto'; import { UpdateCompetitionDto } from '@src/competition/dto/update-competition.dto'; import { Competition } from '@src/competition/entities/competition.entity'; @@ -33,6 +34,22 @@ export class CompetitionService { @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, ) {} + async findAll() { + const competitionList = await this.competitionRepository.find(); + return competitionList.map((competition) => { + return new CompetitionSimpleResponseDto( + competition.id, + competition.name, + competition.detail, + competition.maxParticipants, + competition.startsAt.toISOString(), + competition.endsAt.toISOString(), + competition.createdAt.toISOString(), + competition.updatedAt.toISOString(), + ); + }); + } + async findOne(id: number) { const result = await this.competitionRepository.findOneBy({ id }); if (!result) From a3d9a8eec06f4eb6dfc546a5c6ed48d98d200771 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 22 Nov 2023 18:03:55 +0900 Subject: [PATCH 075/226] =?UTF-8?q?refactor:=20response=20DTO=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/competition.response.dto.ts | 15 +++++++++ .../dto/competition.simple-response.dto.ts | 13 ++++++++ .../services/competition.service.ts | 33 ++----------------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts index f60c7d0..f9b963b 100644 --- a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts @@ -1,6 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; +import { Competition } from '@src/competition/entities/competition.entity'; + export class CompetitionResponseDto { constructor( id: number, @@ -53,4 +55,17 @@ export class CompetitionResponseDto { @ApiProperty({ description: '레코드 수정 일시 (ISO string)' }) @IsNotEmpty() updatedAt: string; + + static from(competition: Competition) { + return new CompetitionResponseDto( + competition.id, + competition.name, + competition.detail, + competition.maxParticipants, + competition.startsAt.toISOString(), + competition.endsAt.toISOString(), + competition.createdAt.toISOString(), + competition.updatedAt.toISOString(), + ); + } } diff --git a/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts index dc2fb50..df3125b 100644 --- a/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/competition.simple-response.dto.ts @@ -55,4 +55,17 @@ export class CompetitionSimpleResponseDto { @ApiProperty({ description: '레코드 수정 일시 (ISO string)' }) @IsNotEmpty() updatedAt: string; + + static from(competition: Competition) { + return new CompetitionSimpleResponseDto( + competition.id, + competition.name, + competition.detail, + competition.maxParticipants, + competition.startsAt.toISOString(), + competition.endsAt.toISOString(), + competition.createdAt.toISOString(), + competition.updatedAt.toISOString(), + ); + } } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index ce9957a..838df84 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -37,16 +37,7 @@ export class CompetitionService { async findAll() { const competitionList = await this.competitionRepository.find(); return competitionList.map((competition) => { - return new CompetitionSimpleResponseDto( - competition.id, - competition.name, - competition.detail, - competition.maxParticipants, - competition.startsAt.toISOString(), - competition.endsAt.toISOString(), - competition.createdAt.toISOString(), - competition.updatedAt.toISOString(), - ); + return CompetitionSimpleResponseDto.from(competition); }); } @@ -54,30 +45,12 @@ export class CompetitionService { const result = await this.competitionRepository.findOneBy({ id }); if (!result) throw new NotFoundException(`대회 id ${id}에 해당하는 대회 정보를 찾을 수 없습니다`); - return new CompetitionResponseDto( - result.id, - result.name, - result.detail, - result.maxParticipants, - result.startsAt.toISOString(), - result.endsAt.toISOString(), - result.createdAt.toISOString(), - result.updatedAt.toISOString(), - ); + return CompetitionResponseDto.from(result); } async create(createCompetitionDto: CreateCompetitionDto) { const result = await this.competitionRepository.save(createCompetitionDto.toEntity()); - return new CompetitionResponseDto( - result.id, - result.name, - result.detail, - result.maxParticipants, - result.startsAt.toISOString(), - result.endsAt.toISOString(), - result.createdAt.toISOString(), - result.updatedAt.toISOString(), - ); + return CompetitionResponseDto.from(result); } async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From 9cbb522d4e572d2e4b0b929638272c6e28f8c9ed Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 22 Nov 2023 18:38:54 +0900 Subject: [PATCH 076/226] =?UTF-8?q?feat:=20=EB=8C=80=ED=9A=8C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API=EA=B0=80=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20id=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EB=8F=84=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/competition.response.dto.ts | 37 +++++++++++++------ .../services/competition.service.ts | 34 +++++++++++++---- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts index f9b963b..f30c7ff 100644 --- a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts @@ -1,8 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; -import { Competition } from '@src/competition/entities/competition.entity'; - export class CompetitionResponseDto { constructor( id: number, @@ -11,6 +9,7 @@ export class CompetitionResponseDto { maxParticipants: number, startsAt: string, endsAt: string, + problems: number[], createdAt: string, updatedAt: string, ) { @@ -20,6 +19,7 @@ export class CompetitionResponseDto { this.maxParticipants = maxParticipants; this.startsAt = startsAt; this.endsAt = endsAt; + this.problemIds = problems; this.createdAt = createdAt; this.updatedAt = updatedAt; } @@ -48,6 +48,10 @@ export class CompetitionResponseDto { @IsNotEmpty() endsAt: string; + @ApiProperty({ description: '대회에 사용되는 문제 id 리스트' }) + @IsNotEmpty() + problemIds: number[]; + @ApiProperty({ description: '레코드 생성 일시 (ISO string)' }) @IsNotEmpty() createdAt: string; @@ -56,16 +60,27 @@ export class CompetitionResponseDto { @IsNotEmpty() updatedAt: string; - static from(competition: Competition) { + static from(args: { + id: number; + name: string; + detail: string; + maxParticipants: number; + startsAt: Date; + endsAt: Date; + problemIds: number[]; + createdAt: Date; + updatedAt: Date; + }) { return new CompetitionResponseDto( - competition.id, - competition.name, - competition.detail, - competition.maxParticipants, - competition.startsAt.toISOString(), - competition.endsAt.toISOString(), - competition.createdAt.toISOString(), - competition.updatedAt.toISOString(), + args.id, + args.name, + args.detail, + args.maxParticipants, + args.startsAt.toISOString(), + args.endsAt.toISOString(), + args.problemIds, + args.createdAt.toISOString(), + args.updatedAt.toISOString(), ); } } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 838df84..3cbd7c4 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,5 +1,5 @@ import { InjectQueue } from '@nestjs/bull'; -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; @@ -41,16 +41,34 @@ export class CompetitionService { }); } - async findOne(id: number) { - const result = await this.competitionRepository.findOneBy({ id }); - if (!result) - throw new NotFoundException(`대회 id ${id}에 해당하는 대회 정보를 찾을 수 없습니다`); - return CompetitionResponseDto.from(result); + async findOne(competitionId: number) { + const competition = await this.competitionRepository.findOneBy({ id: competitionId }); + const problems = await this.competitionProblemRepository.find({ + select: { + problem: { id: true }, + }, + where: { + competition: { id: competitionId }, + }, + relations: { + problem: true, + }, + }); + const problemIds = problems.map((element) => element.problem.id); + if (!competition) + throw new NotFoundException( + `대회 id ${competitionId}에 해당하는 대회 정보를 찾을 수 없습니다`, + ); + if (!problems) + throw new NotFoundException( + `대회 id ${competitionId}에 해당하는 문제 리스트를 찾는 데에 실패했습니다`, + ); + return CompetitionResponseDto.from({ ...competition, problemIds }); } async create(createCompetitionDto: CreateCompetitionDto) { - const result = await this.competitionRepository.save(createCompetitionDto.toEntity()); - return CompetitionResponseDto.from(result); + // const result = await this.competitionRepository.save(createCompetitionDto.toEntity()); + // return CompetitionResponseDto.from(result); } async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From c16b5b1e94360621b46a01ef7853a58fe04c0731 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 11:24:23 +0900 Subject: [PATCH 077/226] =?UTF-8?q?feat:=20=EB=8C=80=ED=9A=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=EC=97=90=EC=84=9C=20=EB=AC=B8=EC=A0=9C=20list?= =?UTF-8?q?=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../competition/dto/create-competition.dto.ts | 16 +++---- .../services/competition.service.ts | 44 +++++++++++++++++-- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index e7a815b..7284b16 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -10,12 +10,14 @@ export class CreateCompetitionDto { maxParticipants: number, startsAt: string, endsAt: string, + problemIds: number[], ) { this.name = name; this.detail = detail; this.maxParticipants = maxParticipants; - this.startsAt = startsAt; + this.problemIds = problemIds; this.endsAt = endsAt; + this.startsAt = startsAt; } @ApiProperty({ description: '대회 이름' }) @@ -38,13 +40,7 @@ export class CreateCompetitionDto { @IsNotEmpty() endsAt: string; - toEntity(): Competition { - const competition = new Competition(); - competition.name = this.name; - competition.detail = this.detail; - competition.maxParticipants = this.maxParticipants; - competition.startsAt = new Date(this.startsAt); - competition.endsAt = new Date(this.endsAt); - return competition; - } + @ApiProperty({ description: '대회에 사용되는 문제 id 리스트' }) + @IsNotEmpty() + problemIds: number[]; } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 3cbd7c4..3f4db76 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -3,7 +3,14 @@ import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; -import { Repository } from 'typeorm'; +import { + DataSource, + EntityManager, + getConnection, + getConnectionManager, + getManager, + Repository, +} from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; @@ -32,6 +39,7 @@ export class CompetitionService { @InjectRepository(CompetitionProblem) private readonly competitionProblemRepository: Repository , @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, + private dataSource: DataSource, ) {} async findAll() { @@ -67,8 +75,38 @@ export class CompetitionService { } async create(createCompetitionDto: CreateCompetitionDto) { - // const result = await this.competitionRepository.save(createCompetitionDto.toEntity()); - // return CompetitionResponseDto.from(result); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + + const problems: CompetitionProblem[] = []; + for (const problemId of createCompetitionDto.problemIds) { + const problem = await this.problemRepository.findOneBy({ id: problemId }); + if (!problem) { + throw new NotFoundException( + `대회를 생성할 때 주어진 문제 리스트 ${JSON.stringify( + createCompetitionDto.problemIds, + )}에 존재하지 않는 문제가 있습니다 (id: ${problemId})`, + ); + } + const competitionProblem = new CompetitionProblem(); + competitionProblem.problem = problem; + problems.push(competitionProblem); + } + + await queryRunner.startTransaction(); + try { + const competition: Competition = await this.competitionRepository.save(createCompetitionDto, { + transaction: false, + }); + for (const competitionProblem of problems) { + competitionProblem.competition = competition; + await this.competitionProblemRepository.save(competitionProblem, { transaction: false }); + } + await queryRunner.commitTransaction(); + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } } async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From d12d7edfcaaac61eae515dc7d788665a45b84c74 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 11:25:16 +0900 Subject: [PATCH 078/226] =?UTF-8?q?chore:=20eslint=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/dto/create-competition.dto.ts | 2 -- .../src/competition/services/competition.service.ts | 11 ++--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index 7284b16..19fe745 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -1,8 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; -import { Competition } from '@src/competition/entities/competition.entity'; - export class CreateCompetitionDto { constructor( name: string, diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 3f4db76..39c75ac 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,16 +1,9 @@ import { InjectQueue } from '@nestjs/bull'; -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; -import { - DataSource, - EntityManager, - getConnection, - getConnectionManager, - getManager, - Repository, -} from 'typeorm'; +import { DataSource, Repository } from 'typeorm'; import { existsSync, readFileSync } from 'fs'; import * as path from 'path'; From 5b3c4f3993a29a0df6f1a04d8209fba6332c2fdb Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 13:25:25 +0900 Subject: [PATCH 079/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=84=B8?= =?UTF-8?q?=EB=B6=80=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20API=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=AC=B8=EC=A0=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=A3=BC=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/competition.response.dto.ts | 4 ---- .../services/competition.service.ts | 18 +----------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts index f30c7ff..7bbbaec 100644 --- a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts @@ -9,7 +9,6 @@ export class CompetitionResponseDto { maxParticipants: number, startsAt: string, endsAt: string, - problems: number[], createdAt: string, updatedAt: string, ) { @@ -19,7 +18,6 @@ export class CompetitionResponseDto { this.maxParticipants = maxParticipants; this.startsAt = startsAt; this.endsAt = endsAt; - this.problemIds = problems; this.createdAt = createdAt; this.updatedAt = updatedAt; } @@ -67,7 +65,6 @@ export class CompetitionResponseDto { maxParticipants: number; startsAt: Date; endsAt: Date; - problemIds: number[]; createdAt: Date; updatedAt: Date; }) { @@ -78,7 +75,6 @@ export class CompetitionResponseDto { args.maxParticipants, args.startsAt.toISOString(), args.endsAt.toISOString(), - args.problemIds, args.createdAt.toISOString(), args.updatedAt.toISOString(), ); diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 39c75ac..406d141 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -44,27 +44,11 @@ export class CompetitionService { async findOne(competitionId: number) { const competition = await this.competitionRepository.findOneBy({ id: competitionId }); - const problems = await this.competitionProblemRepository.find({ - select: { - problem: { id: true }, - }, - where: { - competition: { id: competitionId }, - }, - relations: { - problem: true, - }, - }); - const problemIds = problems.map((element) => element.problem.id); if (!competition) throw new NotFoundException( `대회 id ${competitionId}에 해당하는 대회 정보를 찾을 수 없습니다`, ); - if (!problems) - throw new NotFoundException( - `대회 id ${competitionId}에 해당하는 문제 리스트를 찾는 데에 실패했습니다`, - ); - return CompetitionResponseDto.from({ ...competition, problemIds }); + return CompetitionResponseDto.from(competition); } async create(createCompetitionDto: CreateCompetitionDto) { From 92c44c62182f00cf24d11b2638326b15c61c170a Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 13:33:07 +0900 Subject: [PATCH 080/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20-=20=EB=8C=80=ED=9A=8C=20=EB=8B=B9=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=B6=9C=EC=A0=9C=20=EA=B0=80=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/controllers/competition.controller.ts | 2 +- .../src/competition/services/competition.service.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index bf6c230..24d83ca 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -38,7 +38,7 @@ export class CompetitionController { @Post('/') @ApiOperation({ summary: '대회 생성', - description: `주어진 대회 관련 정보를 이용해 대회를 생성한다.`, + description: `주어진 대회 관련 정보를 이용해 대회를 생성한다. 과도한 DB 접근을 막기 위해, 하나의 대회에서 30개가 넘는 문제를 출제할 수 없도록 정책 상 제한한다.`, }) @ApiResponse({ type: CompetitionResponseDto }) @UsePipes(new ValidationPipe({ transform: true })) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 406d141..a2ab183 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,5 +1,5 @@ import { InjectQueue } from '@nestjs/bull'; -import { Injectable, NotFoundException } from '@nestjs/common'; +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; @@ -52,6 +52,11 @@ export class CompetitionService { } async create(createCompetitionDto: CreateCompetitionDto) { + if (createCompetitionDto.problemIds.length > 30) { + throw new BadRequestException( + `정책 상 하나의 대회에서는 30개가 넘는 문제를 출제할 수 없습니다. (${createCompetitionDto.problemIds.length}개를 출제함)`, + ); + } const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); From 6022067daf1e4ddb124ae587d3634eb05fc83c5e Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 13:56:34 +0900 Subject: [PATCH 081/226] =?UTF-8?q?refactor:=20=EB=8C=80=ED=9A=8C=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20create=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD,=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/competition.service.ts | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index a2ab183..e3251d5 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -52,35 +52,24 @@ export class CompetitionService { } async create(createCompetitionDto: CreateCompetitionDto) { - if (createCompetitionDto.problemIds.length > 30) { - throw new BadRequestException( - `정책 상 하나의 대회에서는 30개가 넘는 문제를 출제할 수 없습니다. (${createCompetitionDto.problemIds.length}개를 출제함)`, - ); - } - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); + this.assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto); - const problems: CompetitionProblem[] = []; + const competitionProblems: CompetitionProblem[] = []; for (const problemId of createCompetitionDto.problemIds) { - const problem = await this.problemRepository.findOneBy({ id: problemId }); - if (!problem) { - throw new NotFoundException( - `대회를 생성할 때 주어진 문제 리스트 ${JSON.stringify( - createCompetitionDto.problemIds, - )}에 존재하지 않는 문제가 있습니다 (id: ${problemId})`, - ); - } + const problem = await this.assertProblemExistsInDb(problemId, createCompetitionDto); const competitionProblem = new CompetitionProblem(); competitionProblem.problem = problem; - problems.push(competitionProblem); + competitionProblems.push(competitionProblem); } + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); await queryRunner.startTransaction(); try { const competition: Competition = await this.competitionRepository.save(createCompetitionDto, { transaction: false, }); - for (const competitionProblem of problems) { + for (const competitionProblem of competitionProblems) { competitionProblem.competition = competition; await this.competitionProblemRepository.save(competitionProblem, { transaction: false }); } @@ -91,6 +80,29 @@ export class CompetitionService { } } + private async assertProblemExistsInDb( + problemId: number, + createCompetitionDto: CreateCompetitionDto, + ) { + const problem = await this.problemRepository.findOneBy({ id: problemId }); + if (!problem) { + throw new NotFoundException( + `대회를 생성할 때 주어진 문제 리스트 ${JSON.stringify( + createCompetitionDto.problemIds, + )}에 존재하지 않는 문제가 있습니다 (id: ${problemId})`, + ); + } + return problem; + } + + private assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto: CreateCompetitionDto) { + if (createCompetitionDto.problemIds.length > 30) { + throw new BadRequestException( + `정책 상 하나의 대회에서는 30개가 넘는 문제를 출제할 수 없습니다. (${createCompetitionDto.problemIds.length}개를 출제함)`, + ); + } + } + async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { const result = await this.competitionRepository.update({ id: id }, { ...updateCompetitionDto }); return !!result.affected; From 929bba1697160714f0a5dbf495ffb92319836e62 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 14:04:04 +0900 Subject: [PATCH 082/226] =?UTF-8?q?refactor:=20=EB=8C=80=ED=9A=8C=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20findOne=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/competition.service.ts | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index e3251d5..169c3ba 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -44,10 +44,7 @@ export class CompetitionService { async findOne(competitionId: number) { const competition = await this.competitionRepository.findOneBy({ id: competitionId }); - if (!competition) - throw new NotFoundException( - `대회 id ${competitionId}에 해당하는 대회 정보를 찾을 수 없습니다`, - ); + this.assertCompetitionExists(competition); return CompetitionResponseDto.from(competition); } @@ -80,29 +77,6 @@ export class CompetitionService { } } - private async assertProblemExistsInDb( - problemId: number, - createCompetitionDto: CreateCompetitionDto, - ) { - const problem = await this.problemRepository.findOneBy({ id: problemId }); - if (!problem) { - throw new NotFoundException( - `대회를 생성할 때 주어진 문제 리스트 ${JSON.stringify( - createCompetitionDto.problemIds, - )}에 존재하지 않는 문제가 있습니다 (id: ${problemId})`, - ); - } - return problem; - } - - private assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto: CreateCompetitionDto) { - if (createCompetitionDto.problemIds.length > 30) { - throw new BadRequestException( - `정책 상 하나의 대회에서는 30개가 넘는 문제를 출제할 수 없습니다. (${createCompetitionDto.problemIds.length}개를 출제함)`, - ); - } - } - async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { const result = await this.competitionRepository.update({ id: id }, { ...updateCompetitionDto }); return !!result.affected; @@ -142,6 +116,7 @@ export class CompetitionService { } // TODO: 유저, 대회 도메인 구현 이후 수정 필요 + async saveScoreResult(scoreResultDto: ScoreResultDto) { const submission = await this.submissionRepository.findOneBy({ id: scoreResultDto.submissionId, @@ -183,4 +158,33 @@ export class CompetitionService { return new ProblemSimpleResponseDto(element.problem.id, element.problem.title); }); } + private assertCompetitionExists(competition: Competition) { + if (!competition) + throw new NotFoundException( + `대회 id ${competition.id}에 해당하는 대회 정보를 찾을 수 없습니다`, + ); + } + + private async assertProblemExistsInDb( + problemId: number, + createCompetitionDto: CreateCompetitionDto, + ) { + const problem = await this.problemRepository.findOneBy({ id: problemId }); + if (!problem) { + throw new NotFoundException( + `대회를 생성할 때 주어진 문제 리스트 ${JSON.stringify( + createCompetitionDto.problemIds, + )}에 존재하지 않는 문제가 있습니다 (id: ${problemId})`, + ); + } + return problem; + } + + private assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto: CreateCompetitionDto) { + if (createCompetitionDto.problemIds.length > 30) { + throw new BadRequestException( + `정책 상 하나의 대회에서는 30개가 넘는 문제를 출제할 수 없습니다. (${createCompetitionDto.problemIds.length}개를 출제함)`, + ); + } + } } From 3b9fbafea3831370e583019ba0d2e528451cf3d6 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:06:08 +0900 Subject: [PATCH 083/226] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index f97a77b..5adac9e 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,6 +1,5 @@ -import { UseFilters, UsePipes, ValidationPipe } from '@nestjs/common'; +import { UsePipes, ValidationPipe } from '@nestjs/common'; import { - BaseWsExceptionFilter, ConnectedSocket, MessageBody, OnGatewayConnection, From 84abe7b2040f96ea3b92b4a26e5bad24cfda7b1c Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:20:07 +0900 Subject: [PATCH 084/226] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=98=ED=99=98=EA=B0=92=20dto=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20entity=EC=97=90=20=EC=9E=88=EB=8A=94=20sw?= =?UTF-8?q?agger=20=EB=8D=B0=EC=BD=94=EB=A0=88=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C,=20=EC=9C=A0=EC=A0=80-=EB=8C=80=ED=9A=8C=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/dto/problem.response.dto.ts | 13 +++++++++++++ .../competition/entities/participant.entity.ts | 6 ++++++ .../src/competition/entities/problem.entity.ts | 12 ------------ .../src/competition/services/problem.service.ts | 15 ++++----------- 4 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 be/algo-with-me-api/src/competition/entities/participant.entity.ts diff --git a/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts b/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts index 76d75fa..4757fa1 100644 --- a/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts @@ -1,5 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Problem } from '../entities/problem.entity'; + export class ProblemResponseDto { constructor( id: number, @@ -34,4 +36,15 @@ export class ProblemResponseDto { @ApiProperty() createdAt: Date; + + static from(problem: Problem, content: string) { + return new ProblemResponseDto( + problem.id, + problem.title, + problem.timeLimit, + problem.memoryLimit, + content, + problem.createdAt, + ); + } } diff --git a/be/algo-with-me-api/src/competition/entities/participant.entity.ts b/be/algo-with-me-api/src/competition/entities/participant.entity.ts new file mode 100644 index 0000000..4bf2345 --- /dev/null +++ b/be/algo-with-me-api/src/competition/entities/participant.entity.ts @@ -0,0 +1,6 @@ +import { Entity } from "typeorm"; + +@Entity() +export class Participant { + +} \ No newline at end of file diff --git a/be/algo-with-me-api/src/competition/entities/problem.entity.ts b/be/algo-with-me-api/src/competition/entities/problem.entity.ts index 06ae38d..6a2792f 100644 --- a/be/algo-with-me-api/src/competition/entities/problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/problem.entity.ts @@ -1,4 +1,3 @@ -import { ApiProperty } from '@nestjs/swagger'; import { Column, CreateDateColumn, @@ -14,46 +13,35 @@ import { Submission } from './submission.entity'; @Entity() export class Problem { @PrimaryGeneratedColumn() - @ApiProperty() id: number; - @ApiProperty() @Column() title: string; - @ApiProperty() @Column() timeLimit: number; - @ApiProperty() @Column() memoryLimit: number; - @ApiProperty() @Column() testcaseNum: number; - @ApiProperty() @Column('text') frameCode: string; - @ApiProperty() @Column('text') solutionCode: string; - @ApiProperty() @OneToMany(() => Submission, (submission) => submission.problem) submissions: Submission[]; - @ApiProperty({ description: '대회문제(competitionProblem) 테이블과 일대다 관계' }) @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.problem) competitionProblems: CompetitionProblem[]; - @ApiProperty() @CreateDateColumn() createdAt: Date; - @ApiProperty() @UpdateDateColumn() updatedAt: Date; } diff --git a/be/algo-with-me-api/src/competition/services/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts index f47376a..a7b3c31 100644 --- a/be/algo-with-me-api/src/competition/services/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -14,11 +14,11 @@ import { Problem } from '../entities/problem.entity'; export class ProblemService { constructor(@InjectRepository(Problem) private readonly problemRepository: Repository ) {} - create(createProblemDto: CreateProblemDto) { + async create(createProblemDto: CreateProblemDto) { const problem: Problem = createProblemDto.toEntity(); - const savedProblem = this.problemRepository.save(problem); - return savedProblem; + const savedProblem = await this.problemRepository.save(problem); + return ProblemResponseDto.from(savedProblem, ''); } async findAll() { @@ -34,14 +34,7 @@ export class ProblemService { const paths = path.join(process.env.PROBLEM_PATH, fileName); if (!existsSync(paths)) throw new NotFoundException('문제 파일을 찾을 수 없습니다.'); const content = readFileSync(paths).toString(); - return new ProblemResponseDto( - problem.id, - problem.title, - problem.timeLimit, - problem.memoryLimit, - content, - problem.createdAt, - ); + return ProblemResponseDto.from(problem, content); } // update(id: number, updateCompetitionDto: UpdateCompetitionDto) { From bc751308cf4673b53aeb55825df9fa6d36cf2368 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:21:53 +0900 Subject: [PATCH 085/226] =?UTF-8?q?fix:=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=97=90=20=EC=9E=88=EB=8A=94=20swagger=20=EB=8D=B0=EC=BD=94?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=ED=84=B0=20=EB=AA=A8=EB=91=90=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/entities/competition.entity.ts | 11 ----------- .../entities/competition.problem.entity.ts | 4 ---- be/algo-with-me-api/src/user/entities/user.entity.ts | 6 ------ 3 files changed, 21 deletions(-) diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts index c9cd73f..370fc82 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -1,4 +1,3 @@ -import { ApiProperty } from '@nestjs/swagger'; import { Column, CreateDateColumn, @@ -14,42 +13,32 @@ import { Submission } from './submission.entity'; @Entity() export class Competition { @PrimaryGeneratedColumn() - @ApiProperty({ description: '대회 id' }) id: number; - @ApiProperty({ description: '대회 이름' }) @Column() name: string; - @ApiProperty({ description: '대회에 대한 설명글' }) @Column('text') detail: string; - @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) @Column() maxParticipants: number; - @ApiProperty({ description: '대회 시작 일시' }) @Column() startsAt: Date; - @ApiProperty({ description: '대회 종료 일시' }) @Column() endsAt: Date; - @ApiProperty({ description: '제출(submission) 테이블과 일대다 관계' }) @OneToMany(() => Submission, (submission) => submission.competition) submissions: Submission[]; - @ApiProperty({ description: '대회문제(competitionProblem) 테이블과 일대다 관계' }) @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) competitionProblems: CompetitionProblem[]; - @ApiProperty({ description: '레코드 생성 일시' }) @CreateDateColumn() createdAt: Date; - @ApiProperty({ description: '레코드 수정 일시' }) @UpdateDateColumn() updatedAt: Date; } diff --git a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts index 95cbb8b..02dde8c 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts @@ -1,4 +1,3 @@ -import { ApiProperty } from '@nestjs/swagger'; import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { Competition } from './competition.entity'; @@ -7,14 +6,11 @@ import { Problem } from './problem.entity'; @Entity() export class CompetitionProblem { @PrimaryGeneratedColumn() - @ApiProperty({ description: 'id' }) id: number; - @ApiProperty({ description: '대회(competition) 테이블과 다대일 관계' }) @ManyToOne(() => Competition, (competition) => competition.competitionProblems) competition: Competition; - @ApiProperty({ description: '문제(problem) 테이블과 다대일 관계' }) @ManyToOne(() => Problem, (problem) => problem.competitionProblems) problem: Problem; diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index 799b762..e7d80d1 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -1,4 +1,3 @@ -import { ApiProperty } from '@nestjs/swagger'; import { IsEmail } from 'class-validator'; import { Column, @@ -10,24 +9,19 @@ import { @Entity() export class User { - @ApiProperty({ description: '유저 id' }) @PrimaryGeneratedColumn() id: number; - @ApiProperty({ description: '이메일' }) @Column() @IsEmail() email: string; - @ApiProperty({ description: '닉네임 초기값은 이메일' }) @Column() nickname: string; - @ApiProperty({ description: '생성일' }) @CreateDateColumn() createdAt: Date; - @ApiProperty({ description: '수정일' }) @UpdateDateColumn() updatedAt: Date; } From d51fd885551cb69789a9de4a4f461e030061455c Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:30:15 +0900 Subject: [PATCH 086/226] =?UTF-8?q?feat:=20=EB=8C=80=ED=9A=8C=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=EC=9E=90=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/competition.entity.ts | 7 ++++++ .../entities/participant.entity.ts | 22 +++++++++++++++---- .../src/user/entities/user.entity.ts | 6 +++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts index 370fc82..b9cb136 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -8,6 +8,7 @@ import { } from 'typeorm'; import { CompetitionProblem } from './competition.problem.entity'; +import { CompetitionParticipant } from './participant.entity'; import { Submission } from './submission.entity'; @Entity() @@ -36,6 +37,12 @@ export class Competition { @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) competitionProblems: CompetitionProblem[]; + @OneToMany( + () => CompetitionParticipant, + (competitionParticipant) => competitionParticipant.competition, + ) + competitionParticipants: CompetitionParticipant[]; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-api/src/competition/entities/participant.entity.ts b/be/algo-with-me-api/src/competition/entities/participant.entity.ts index 4bf2345..11e011a 100644 --- a/be/algo-with-me-api/src/competition/entities/participant.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/participant.entity.ts @@ -1,6 +1,20 @@ -import { Entity } from "typeorm"; +import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; + +import { Competition } from './competition.entity'; + +import { User } from '@src/user/entities/user.entity'; @Entity() -export class Participant { - -} \ No newline at end of file +export class CompetitionParticipant { + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(() => User, (user) => user.participants) + user: User; + + @ManyToOne(() => Competition, (competition) => competition) + competition: Competition; + + @CreateDateColumn() + createdAt: Date; +} diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index e7d80d1..cc0b99d 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -3,10 +3,13 @@ import { Column, CreateDateColumn, Entity, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { CompetitionParticipant } from '@src/competition/entities/participant.entity'; + @Entity() export class User { @PrimaryGeneratedColumn() @@ -19,6 +22,9 @@ export class User { @Column() nickname: string; + @OneToMany(() => CompetitionParticipant, (competitionParticipant) => competitionParticipant.user) + participants: CompetitionParticipant[]; + @CreateDateColumn() createdAt: Date; From 7a824ecde4e60abb4541ded7d2514dfbd63084d1 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 15:25:14 +0900 Subject: [PATCH 087/226] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EB=8C=80?= =?UTF-8?q?=ED=9A=8C=20=EC=B0=B8=EA=B0=80=20api=20=EA=B5=AC=ED=98=84,=20sw?= =?UTF-8?q?agger=EC=97=90=20=ED=86=A0=ED=81=B0=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 10 ++++++- .../src/competition/competition.module.ts | 11 +++++++- .../controllers/competition.controller.ts | 27 +++++++++++++++++-- .../entities/competition.entity.ts | 2 +- ...y.ts => competition.participant.entity.ts} | 3 ++- .../services/competition.service.ts | 22 +++++++++++++++ be/algo-with-me-api/src/main.ts | 3 ++- .../src/user/entities/user.entity.ts | 2 +- 8 files changed, 72 insertions(+), 8 deletions(-) rename be/algo-with-me-api/src/competition/entities/{participant.entity.ts => competition.participant.entity.ts} (84%) diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 0298e2f..61f0d07 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -6,6 +6,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; import { CompetitionModule } from './competition/competition.module'; import { CompetitionProblem } from './competition/entities/competition.problem.entity'; +import { CompetitionParticipant } from './competition/entities/competition.participant.entity'; import { Problem } from './competition/entities/problem.entity'; import { Submission } from './competition/entities/submission.entity'; import { User } from './user/entities/user.entity'; @@ -27,7 +28,14 @@ import { Competition } from '@src/competition/entities/competition.entity'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: true, - entities: [Problem, Submission, Competition, User, CompetitionProblem], + entities: [ + Problem, + Submission, + Competition, + User, + CompetitionProblem, + CompetitionParticipant, + ], logging: true, }), BullModule.forRoot({ diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 9d91a53..0756d8e 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -5,6 +5,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CompetitionController } from './controllers/competition.controller'; import { ProblemController } from './controllers/problem.controller'; import { CompetitionProblem } from './entities/competition.problem.entity'; +import { CompetitionParticipant } from './entities/competition.participant.entity'; import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; import { CompetitionGateWay } from './gateways/competition.gateway'; @@ -13,10 +14,18 @@ import { ProblemService } from './services/problem.service'; import { SubmissionConsumer } from './tem.consumer'; import { Competition } from '@src/competition/entities/competition.entity'; +import { User } from '@src/user/entities/user.entity'; @Module({ imports: [ - TypeOrmModule.forFeature([Problem, Submission, Competition, CompetitionProblem]), + TypeOrmModule.forFeature([ + Problem, + Submission, + Competition, + CompetitionProblem, + CompetitionParticipant, + User, + ]), BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 24d83ca..71e05ec 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -1,5 +1,17 @@ -import { Body, Controller, Get, Param, Post, Put, UsePipes, ValidationPipe } from '@nestjs/common'; -import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { + Body, + Controller, + Get, + Param, + Post, + Put, + Req, + UseGuards, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; import { CreateCompetitionDto } from '../dto/create-competition.dto'; @@ -85,6 +97,17 @@ export class CompetitionController { this.competitionService.saveScoreResult(scoreResultDto); } + @Post('/:competitionId/participations') + @ApiOperation({ + summary: '대회 참여 api', + description: '유저가 대회에 참여하는 api 입니다.', + }) + @ApiBearerAuth() + @UseGuards(AuthGuard('jwt')) + joinCompetition(@Req() req, @Param('competitionId') competitionId: number) { + this.competitionService.joinCompetition(competitionId, req.user.email); + } + // @Post('submissions') // @UsePipes(new ValidationPipe({ transform: true })) // createSubmission(@Body() createSubmissionDto: CreateSubmissionDto) { diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts index b9cb136..7105e76 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -7,8 +7,8 @@ import { UpdateDateColumn, } from 'typeorm'; +import { CompetitionParticipant } from './competition.participant.entity'; import { CompetitionProblem } from './competition.problem.entity'; -import { CompetitionParticipant } from './participant.entity'; import { Submission } from './submission.entity'; @Entity() diff --git a/be/algo-with-me-api/src/competition/entities/participant.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts similarity index 84% rename from be/algo-with-me-api/src/competition/entities/participant.entity.ts rename to be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts index 11e011a..72a0fb1 100644 --- a/be/algo-with-me-api/src/competition/entities/participant.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts @@ -1,10 +1,11 @@ -import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, Unique } from 'typeorm'; import { Competition } from './competition.entity'; import { User } from '@src/user/entities/user.entity'; @Entity() +@Unique('unique_participant', ['user', 'competition']) export class CompetitionParticipant { @PrimaryGeneratedColumn() id: number; diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 169c3ba..9e43481 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -12,6 +12,7 @@ import { CompetitionProblemResponseDto } from '../dto/competition.problem.respon import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; +import { CompetitionParticipant } from '../entities/competition.participant.entity'; import { CompetitionProblem } from '../entities/competition.problem.entity'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; @@ -21,6 +22,7 @@ import { CompetitionSimpleResponseDto } from '@src/competition/dto/competition.s import { CreateCompetitionDto } from '@src/competition/dto/create-competition.dto'; import { UpdateCompetitionDto } from '@src/competition/dto/update-competition.dto'; import { Competition } from '@src/competition/entities/competition.entity'; +import { User } from '@src/user/entities/user.entity'; @Injectable() export class CompetitionService { @@ -31,6 +33,9 @@ export class CompetitionService { @InjectRepository(Submission) private readonly submissionRepository: Repository , @InjectRepository(CompetitionProblem) private readonly competitionProblemRepository: Repository , + @InjectRepository(User) private readonly userRepository: Repository , + @InjectRepository(CompetitionParticipant) + private readonly competitionParticipantRepository: Repository , @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, private dataSource: DataSource, ) {} @@ -100,6 +105,23 @@ export class CompetitionService { ); } + async joinCompetition(competitionId: number, email: string) { + const competition: Competition = await this.competitionRepository.findOneBy({ + id: competitionId, + }); + this.assertCompetitionExists(competition); + const user: User = await this.userRepository.findOneBy({ email: email }); + if (!user) throw new NotFoundException('찾을 수 없는 유저입니다.'); + console.log(user); + + const isExist: CompetitionParticipant = await this.competitionParticipantRepository.findOneBy({ + competition: competition, + user: user, + }); + if (!isExist) throw new BadRequestException('이미 참여중인 유저입니다.'); + this.competitionParticipantRepository.save({ competition: competition, user: user }); + } + async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string) { const problem: Problem = await this.problemRepository.findOneBy({ id: createSubmissionDto.problemId, diff --git a/be/algo-with-me-api/src/main.ts b/be/algo-with-me-api/src/main.ts index 789c979..44aa50c 100644 --- a/be/algo-with-me-api/src/main.ts +++ b/be/algo-with-me-api/src/main.ts @@ -9,6 +9,7 @@ function setSwagger(app: INestApplication ) { .setTitle('algo-with-me-api') .setDescription('algo with me API description') .setVersion('1.0') + .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, config); @@ -16,7 +17,7 @@ function setSwagger(app: INestApplication ) { } async function bootstrap() { - const app = await NestFactory.create(AppModule, {cors: true}); + const app = await NestFactory.create(AppModule, { cors: true }); setSwagger(app); await app.listen(3000); } diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index cc0b99d..7504f2a 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -8,7 +8,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { CompetitionParticipant } from '@src/competition/entities/participant.entity'; +import { CompetitionParticipant } from '@src/competition/entities/competition.participant.entity'; @Entity() export class User { From 8079cc1045c897facf9e8bc4d57cf7a3c21c0432 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:30:37 +0900 Subject: [PATCH 088/226] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=A7=81=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84,?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9D=80=20=EC=95=88=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 2 +- .../controllers/competition.controller.ts | 12 +++++------ .../src/exception/exception.enum.ts | 4 ++++ .../src/exception/service.exception.filter.ts | 20 +++++++++++++++++++ .../src/exception/service.exception.ts | 17 ++++++++++++++++ be/algo-with-me-api/src/main.ts | 3 +++ 6 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 be/algo-with-me-api/src/exception/exception.enum.ts create mode 100644 be/algo-with-me-api/src/exception/service.exception.filter.ts create mode 100644 be/algo-with-me-api/src/exception/service.exception.ts diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index 61f0d07..db87035 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -5,8 +5,8 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; import { CompetitionModule } from './competition/competition.module'; -import { CompetitionProblem } from './competition/entities/competition.problem.entity'; import { CompetitionParticipant } from './competition/entities/competition.participant.entity'; +import { CompetitionProblem } from './competition/entities/competition.problem.entity'; import { Problem } from './competition/entities/problem.entity'; import { Submission } from './competition/entities/submission.entity'; import { User } from './user/entities/user.entity'; diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 71e05ec..3b9addf 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -82,8 +82,8 @@ export class CompetitionController { @Get('problems/:problemId') @ApiOperation({ summary: '대회 문제 상세 조회' }) @ApiResponse({ type: CompetitionProblemResponseDto }) - findOneProblem(@Param('problemId') problemId: number) { - return this.competitionService.findOneProblem(problemId); + async findOneProblem(@Param('problemId') problemId: number) { + return await this.competitionService.findOneProblem(problemId); } @Post('scores') @@ -93,8 +93,8 @@ export class CompetitionController { '**[프론트엔드에서는 사용되지 않음. 백엔드에서만 사용됨]** 채점 서버에서 테스트케이스 별로 채점이 완료될경우 호출할 api', }) @UsePipes(new ValidationPipe({ transform: true })) - saveScoreResult(@Body() scoreResultDto: ScoreResultDto) { - this.competitionService.saveScoreResult(scoreResultDto); + async saveScoreResult(@Body() scoreResultDto: ScoreResultDto) { + await this.competitionService.saveScoreResult(scoreResultDto); } @Post('/:competitionId/participations') @@ -104,8 +104,8 @@ export class CompetitionController { }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt')) - joinCompetition(@Req() req, @Param('competitionId') competitionId: number) { - this.competitionService.joinCompetition(competitionId, req.user.email); + async joinCompetition(@Req() req, @Param('competitionId') competitionId: number) { + await this.competitionService.joinCompetition(competitionId, req.user.email); } // @Post('submissions') diff --git a/be/algo-with-me-api/src/exception/exception.enum.ts b/be/algo-with-me-api/src/exception/exception.enum.ts new file mode 100644 index 0000000..f365726 --- /dev/null +++ b/be/algo-with-me-api/src/exception/exception.enum.ts @@ -0,0 +1,4 @@ +export const ERROR_CODE = { + NOT_FOUND: 404, + BAD_REQUEST: 400, +} as const; diff --git a/be/algo-with-me-api/src/exception/service.exception.filter.ts b/be/algo-with-me-api/src/exception/service.exception.filter.ts new file mode 100644 index 0000000..48533a2 --- /dev/null +++ b/be/algo-with-me-api/src/exception/service.exception.filter.ts @@ -0,0 +1,20 @@ +import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common'; +import { Request, Response } from 'express'; + +import { ServiceException } from './service.exception'; + +@Catch(ServiceException) +export class ServiceExceptionFilter implements ExceptionFilter { + catch(exception: ServiceException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const request = ctx.getRequest (); + const response = ctx.getResponse (); + const status = exception.errorCode; + + response.status(status).json({ + statusCode: status, + message: exception.message, + path: request.url, + }); + } +} diff --git a/be/algo-with-me-api/src/exception/service.exception.ts b/be/algo-with-me-api/src/exception/service.exception.ts new file mode 100644 index 0000000..3127c49 --- /dev/null +++ b/be/algo-with-me-api/src/exception/service.exception.ts @@ -0,0 +1,17 @@ +import { ERROR_CODE } from './exception.enum'; + +export const NotFoundException = (message?: string) => { + return new ServiceException(ERROR_CODE.NOT_FOUND, message); +}; + +export const BadRequestException = (message?: string) => { + return new ServiceException(ERROR_CODE.BAD_REQUEST, message); +}; + +export class ServiceException extends Error { + errorCode: number; + constructor(errorCode: number, message?: string) { + super(message); + this.errorCode = errorCode; + } +} diff --git a/be/algo-with-me-api/src/main.ts b/be/algo-with-me-api/src/main.ts index 44aa50c..dd5736c 100644 --- a/be/algo-with-me-api/src/main.ts +++ b/be/algo-with-me-api/src/main.ts @@ -2,6 +2,8 @@ import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { ServiceExceptionFilter } from './exception/service.exception.filter'; + import { AppModule } from '@src/app.module'; function setSwagger(app: INestApplication ) { @@ -18,6 +20,7 @@ function setSwagger(app: INestApplication ) { async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); + // app.useGlobalFilters(new ServiceExceptionFilter()); setSwagger(app); await app.listen(3000); } From 3197f1dad90c88270bcc55c1aacc5ff6170d39ae Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:42:38 +0900 Subject: [PATCH 089/226] =?UTF-8?q?fix:=20=EC=95=88=EC=93=B0=EB=8A=94=20im?= =?UTF-8?q?port=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/main.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/be/algo-with-me-api/src/main.ts b/be/algo-with-me-api/src/main.ts index dd5736c..0ea6084 100644 --- a/be/algo-with-me-api/src/main.ts +++ b/be/algo-with-me-api/src/main.ts @@ -2,8 +2,6 @@ import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { ServiceExceptionFilter } from './exception/service.exception.filter'; - import { AppModule } from '@src/app.module'; function setSwagger(app: INestApplication ) { From b5cae22478f19383693be450ad91cbbb4e525a22 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:44:43 +0900 Subject: [PATCH 090/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20entity=20nullable=20false=20=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../competition/entities/competition.participant.entity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts index 72a0fb1..68fed50 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts @@ -10,10 +10,10 @@ export class CompetitionParticipant { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => User, (user) => user.participants) + @ManyToOne(() => User, (user) => user.participants, {nullable: false}) user: User; - @ManyToOne(() => Competition, (competition) => competition) + @ManyToOne(() => Competition, (competition) => competition, {nullable: false}) competition: Competition; @CreateDateColumn() From 54f893adc254eb9f066a0451495c40a5ab7d3458 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:51:49 +0900 Subject: [PATCH 091/226] =?UTF-8?q?fix:=20eslint=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/competition/competition.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index 0756d8e..b0507bb 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -4,8 +4,8 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CompetitionController } from './controllers/competition.controller'; import { ProblemController } from './controllers/problem.controller'; -import { CompetitionProblem } from './entities/competition.problem.entity'; import { CompetitionParticipant } from './entities/competition.participant.entity'; +import { CompetitionProblem } from './entities/competition.problem.entity'; import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; import { CompetitionGateWay } from './gateways/competition.gateway'; From eca60a5b14119cc8c3d10a4afc47750527c10437 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:44:53 +0900 Subject: [PATCH 092/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=EC=9A=94=EC=B2=AD=20=EC=A4=91=EB=B3=B5=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/competition.participant.entity.ts | 4 ++-- .../competition/services/competition.service.ts | 14 ++++++++------ .../src/user/entities/user.entity.ts | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts index 68fed50..dfb586b 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts @@ -10,10 +10,10 @@ export class CompetitionParticipant { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => User, (user) => user.participants, {nullable: false}) + @ManyToOne(() => User, (user) => user.competitionParticipant, {nullable: false}) user: User; - @ManyToOne(() => Competition, (competition) => competition, {nullable: false}) + @ManyToOne(() => Competition, (competition) => competition.competitionParticipants, {nullable: false}) competition: Competition; @CreateDateColumn() diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 9e43481..7174ca7 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -112,13 +112,15 @@ export class CompetitionService { this.assertCompetitionExists(competition); const user: User = await this.userRepository.findOneBy({ email: email }); if (!user) throw new NotFoundException('찾을 수 없는 유저입니다.'); - console.log(user); - const isExist: CompetitionParticipant = await this.competitionParticipantRepository.findOneBy({ - competition: competition, - user: user, - }); - if (!isExist) throw new BadRequestException('이미 참여중인 유저입니다.'); + const isAlreadyJoined: CompetitionParticipant[] = + await this.competitionParticipantRepository.find({ + where: { + competition: { id: competition.id }, + user: { id: user.id }, + }, + }); + if (isAlreadyJoined.length !== 0) throw new BadRequestException('이미 참여중인 유저입니다.'); this.competitionParticipantRepository.save({ competition: competition, user: user }); } diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index 7504f2a..16f3db4 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -23,7 +23,7 @@ export class User { nickname: string; @OneToMany(() => CompetitionParticipant, (competitionParticipant) => competitionParticipant.user) - participants: CompetitionParticipant[]; + competitionParticipant: CompetitionParticipant[]; @CreateDateColumn() createdAt: Date; From 24c7fe06aeb1ed30994d648a4f2fa294f9c598e5 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 18:14:35 +0900 Subject: [PATCH 093/226] =?UTF-8?q?fix:=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=97=90=20=EC=99=B8=EB=9E=98=ED=82=A4=20id=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../competition.participant.entity.ts | 21 ++++++++++++++++--- .../entities/competition.problem.entity.ts | 8 ++++++- .../competition/entities/submission.entity.ts | 8 ++++++- .../services/competition.service.ts | 4 ++-- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts index dfb586b..1c8fc54 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.participant.entity.ts @@ -1,4 +1,11 @@ -import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, Unique } from 'typeorm'; +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + Unique, +} from 'typeorm'; import { Competition } from './competition.entity'; @@ -10,10 +17,18 @@ export class CompetitionParticipant { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => User, (user) => user.competitionParticipant, {nullable: false}) + @Column() + userId: number; + + @ManyToOne(() => User, (user) => user.competitionParticipant, { nullable: false }) user: User; - @ManyToOne(() => Competition, (competition) => competition.competitionParticipants, {nullable: false}) + @Column() + competitionId: number; + + @ManyToOne(() => Competition, (competition) => competition.competitionParticipants, { + nullable: false, + }) competition: Competition; @CreateDateColumn() diff --git a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts index 02dde8c..1aad3cf 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts @@ -1,4 +1,4 @@ -import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { Competition } from './competition.entity'; import { Problem } from './problem.entity'; @@ -8,9 +8,15 @@ export class CompetitionProblem { @PrimaryGeneratedColumn() id: number; + @Column() + competitionId: number; + @ManyToOne(() => Competition, (competition) => competition.competitionProblems) competition: Competition; + @Column() + problemId: number; + @ManyToOne(() => Problem, (problem) => problem.competitionProblems) problem: Problem; diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts index 98a52ad..edf22db 100644 --- a/be/algo-with-me-api/src/competition/entities/submission.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -29,10 +29,16 @@ export class Submission { @Column('json', { nullable: true, default: [] }) detail: object[]; + @Column() + problemId: number; + @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) problem: Problem; - @ManyToOne(() => Competition, (competition) => competition.submissions) + @Column() + competitionId: number; + + @ManyToOne(() => Competition, (competition) => competition.submissions, {nullable: false}) competition: Competition; @CreateDateColumn() diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 7174ca7..c915e36 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -116,8 +116,8 @@ export class CompetitionService { const isAlreadyJoined: CompetitionParticipant[] = await this.competitionParticipantRepository.find({ where: { - competition: { id: competition.id }, - user: { id: user.id }, + competitionId: competition.id, + userId: user.id, }, }); if (isAlreadyJoined.length !== 0) throw new BadRequestException('이미 참여중인 유저입니다.'); From 51b5a785a0d7241a05af523ff8e6a5ce0200face Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 22 Nov 2023 17:41:10 +0900 Subject: [PATCH 094/226] =?UTF-8?q?fix:=20=EC=B1=84=EC=A0=90=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20API=20=EB=AC=B8=EC=84=9C=20=EC=8A=A4=ED=8E=99?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EB=B0=94=EA=BE=B8=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.controller.ts | 11 +- be/algo-with-me-score/src/app.module.ts | 9 +- be/algo-with-me-score/src/app.service.ts | 7 +- .../src/score/dtos/message-queue-item.dto.ts | 11 ++ .../src/score/dtos/score-result.dto.ts | 38 ++++++ .../interfaces/score-result.interface.ts | 16 --- .../src/score/services/filesystem.service.ts | 27 +++-- .../src/score/services/score.consumer.ts | 39 +++--- .../src/score/services/score.service.ts | 113 +++++++++++------- 9 files changed, 177 insertions(+), 94 deletions(-) create mode 100644 be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts create mode 100644 be/algo-with-me-score/src/score/dtos/score-result.dto.ts delete mode 100644 be/algo-with-me-score/src/score/interfaces/score-result.interface.ts diff --git a/be/algo-with-me-score/src/app.controller.ts b/be/algo-with-me-score/src/app.controller.ts index 39decf9..ec3105a 100644 --- a/be/algo-with-me-score/src/app.controller.ts +++ b/be/algo-with-me-score/src/app.controller.ts @@ -1,13 +1,20 @@ import { Body, Controller, Post } from '@nestjs/common'; import { AppService } from './app.service'; +import { MessageQueueItemDto } from './score/dtos/message-queue-item.dto'; @Controller() export class AppController { constructor(private readonly appService: AppService) {} @Post() - addMessage(@Body('submissionId') submissionId: number) { - return this.appService.addMessageQueue(submissionId); + addMessage( + @Body('submissionId') submissionId: number, + @Body('problemId') problemId: number, + @Body('sessionId') sessionId: string, + ) { + return this.appService.addMessageQueue( + new MessageQueueItemDto(submissionId, problemId, sessionId), + ); } } diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index b636e71..5b33591 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -5,6 +5,8 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import * as process from 'node:process'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; import { Problem } from './score/entities/problem.entity'; import { Submission } from './score/entities/submission.entity'; import { ScoreModule } from './score/score.module'; @@ -32,9 +34,12 @@ import { ScoreModule } from './score/score.module'; password: process.env.REDIS_PASSWORD, }, }), + BullModule.registerQueue({ + name: 'testQueue', + }), ScoreModule, ], - controllers: [], - providers: [], + controllers: [AppController], + providers: [AppService], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts index 4789a20..7a705c3 100644 --- a/be/algo-with-me-score/src/app.service.ts +++ b/be/algo-with-me-score/src/app.service.ts @@ -2,12 +2,13 @@ import { InjectQueue } from '@nestjs/bull'; import { Injectable } from '@nestjs/common'; import { Queue } from 'bull'; +import { MessageQueueItemDto } from './score/dtos/message-queue-item.dto'; + @Injectable() export class AppService { constructor(@InjectQueue('testQueue') private testQueue: Queue) {} - async addMessageQueue(data: number) { - const job = await this.testQueue.add('score', { submissionId: data }); - return job; + async addMessageQueue(item: MessageQueueItemDto) { + return await this.testQueue.add('score', item); } } diff --git a/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts b/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts new file mode 100644 index 0000000..02ccbee --- /dev/null +++ b/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts @@ -0,0 +1,11 @@ +export class MessageQueueItemDto { + constructor(submissionId: number, problemId: number, sessionId: string) { + this.submissionId = submissionId; + this.problemId = problemId; + this.sessionId = sessionId; + } + + submissionId: number; + problemId: number; + sessionId: string; +} diff --git a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts new file mode 100644 index 0000000..6be0685 --- /dev/null +++ b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts @@ -0,0 +1,38 @@ +import { RESULT } from '../entities/submission.enums'; + +export class ScoreResultDto { + constructor( + submissionId: number, + competitionId: number, + userId: number, + problemId: number, + testcaseId: number, + result: keyof typeof RESULT, + stdout: string, + stderr: string, + timeUsage: number, + memoryUsage: number, + ) { + this.submissionId = submissionId; + this.competitionId = competitionId; + this.userId = userId; + this.problemId = problemId; + this.testcaseId = testcaseId; + this.result = result; + this.stdout = stdout; + this.stderr = stderr; + this.timeUsage = timeUsage; + this.memoryUsage = memoryUsage; + } + + submissionId: number; + competitionId: number; + userId: number; + problemId: number; + testcaseId: number; + result: keyof typeof RESULT; + stdout: string; + stderr: string; + timeUsage: number; + memoryUsage: number; +} diff --git a/be/algo-with-me-score/src/score/interfaces/score-result.interface.ts b/be/algo-with-me-score/src/score/interfaces/score-result.interface.ts deleted file mode 100644 index 8209aaa..0000000 --- a/be/algo-with-me-score/src/score/interfaces/score-result.interface.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { RESULT } from '../entities/submission.enums'; - -interface IScoreResult { - submissionId: number; - competitionId: number; - userId: number; - problemId: number; - testcaseNo: number; - result: keyof typeof RESULT; - stdout: string | Buffer; - stderr: string | Buffer; - timeUsage: number; - memoryUsage: number; -} - -export default IScoreResult; diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 741b067..22c806d 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -1,20 +1,23 @@ +import { InternalServerErrorException, Logger } from '@nestjs/common'; +import { Repository } from 'typeorm'; + import fs from 'node:fs'; import process from 'process'; -import { Submission } from '../entities/submission.entity'; +import { Problem } from '../entities/problem.entity'; export class FilesystemService { - public writeSubmittedCode( - competitionId: number, - userId: number, - problemId: number, - submission: Submission, - ) { - const mergedCode = this.getMergedCode(submission.code, submission.problem.frameCode); - fs.writeFileSync( - `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}`, - mergedCode, - ); + constructor(private readonly problemRepository: Repository ) {} + + public writeSubmittedCode(competitionId: number, userId: number, problemId: number) { + // TODO: code와 프레임코드 조인해서 가져오기 + const code = ''; + const frameCode = ''; + const mergedCode = this.getMergedCode(code, frameCode); + const filepath = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}`; + if (!fs.existsSync(filepath)) + throw new InternalServerErrorException(`경로 ${filepath}가 없습니다`); + fs.writeFileSync(filepath, mergedCode); } private getMergedCode(code: string, frameCode: string) { diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 4a691dc..87c18a2 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -1,4 +1,5 @@ import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +import { InternalServerErrorException, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Job } from 'bull'; import { Repository } from 'typeorm'; @@ -7,10 +8,11 @@ import * as process from 'process'; import { FilesystemService } from './filesystem.service'; import { ScoreService } from './score.service'; +import { MessageQueueItemDto } from '../dtos/message-queue-item.dto'; import { Submission } from '../entities/submission.entity'; -import IScoreResult from '../interfaces/score-result.interface'; -@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +// @Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +@Processor('testQueue') export class SubmissionConsumer { constructor( @InjectRepository(Submission) private readonly submissionRepository: Repository , @@ -20,40 +22,39 @@ export class SubmissionConsumer { @Process('score') async getMessageQueue(job: Job) { - const submissionId = job.data.submissionId; + const messageQueueItem = new MessageQueueItemDto( + job.data.submissionId, + job.data.problemId, + job.data.sessionId, + ); + const logger = new Logger(); + const submissionId = messageQueueItem.submissionId; const submission = await this.submissionRepository.findOneBy({ id: submissionId }); + if (!submission) + throw new InternalServerErrorException( + `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`, + ); const { competitionId, userId, problemId } = this.getIds(submission); - this.filesystemService.writeSubmittedCode(competitionId, userId, problemId, submission); + this.filesystemService.writeSubmittedCode(competitionId, userId, problemId); - const scoreResults = await this.scoreService.score( + await this.scoreService.scoreAllAndSendResult( submission, submissionId, competitionId, userId, problemId, ); - - await this.sendScoreResults(submissionId, scoreResults); } private getIds(submission: Submission) { + // TODO: submission으로부터 competitionId, problemId, userId를 받을 수 있어야 함 const competitionId = 1; - const userId = 2; - const problemId = submission.problem.id; + const userId = 1; + const problemId = 1; return { competitionId, userId, problemId }; } - private async sendScoreResults(submissionId: number, codeRunResult: IScoreResult[]) { - await fetch(`http://localhost:3000/scores`, { - method: 'POST', - body: JSON.stringify({ - submissionId, - codeRunResult, - }), - }); - } - @OnQueueCompleted() async onCompleted(job: Job) { await job.remove(); diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 03f1bfc..0ad68f0 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -1,74 +1,87 @@ +import { InternalServerErrorException, Logger } from '@nestjs/common'; + import fs from 'node:fs'; import process from 'process'; +import { ScoreResultDto } from '../dtos/score-result.dto'; import { Submission } from '../entities/submission.entity'; import { RESULT } from '../entities/submission.enums'; import ICoderunResponse from '../interfaces/coderun-response.interface'; -import IScoreResult from '../interfaces/score-result.interface'; export class ScoreService { - public async score( + public async scoreAllAndSendResult( submission: Submission, submissionId: number, competitionId: number, userId: number, problemId: number, ) { - const scoreResults: IScoreResult[] = []; - for (let testcaseNo = 0; testcaseNo < submission.problem.testcaseNum; testcaseNo++) { - await this.scoreOneTestcase( + for (let testcaseId = 1; testcaseId <= submission.problem.testcaseNum; testcaseId++) { + await this.scoreOneTestcaseAndSendResult( submissionId, competitionId, userId, problemId, - testcaseNo, - scoreResults, + testcaseId, ); } - return scoreResults; } - private async scoreOneTestcase( + private async scoreOneTestcaseAndSendResult( submissionId: number, competitionId: number, userId: number, problemId: number, - testcaseNo: number, - scoreResults: IScoreResult[], + testcaseId: number, ) { - const codeRunResponse = await this.runCode(competitionId, userId, problemId, testcaseNo); + const codeRunResponse = await this.runCode(competitionId, userId, problemId, testcaseId); const { result: codeRunOutput, stdout, stderr, - } = this.getCodeRunOutputs(competitionId, userId, problemId, testcaseNo); - const testcaseAnswer = this.getTestcaseAnswer(problemId, testcaseNo); - const scoreResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); + } = this.getCodeRunOutputs(competitionId, userId, problemId, testcaseId); + const testcaseAnswer = this.getTestcaseAnswer(problemId, testcaseId); + const judgeResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); - scoreResults.push({ - submissionId, - competitionId, - userId, - problemId, - testcaseNo, - result: scoreResult, - stdout, - stderr, - timeUsage: null, - memoryUsage: null, - }); + await this.sendScoreResult( + new ScoreResultDto( + submissionId, + competitionId, + userId, + problemId, + testcaseId, + judgeResult, + stdout, + stderr, + 0, + 0, + ), + ); + } + + private async sendScoreResult(scoreResult: ScoreResultDto) { + const logger = new Logger(); + logger.debug(JSON.stringify(scoreResult)); + await fetch( + `http://${process.env.API_SERVER_HOST}:${process.env.API_SERVER_PORT}/competitions/scores`, + { + method: 'POST', + body: JSON.stringify(scoreResult), + }, + ); } private async runCode( competitionId: number, userId: number, problemId: number, - testcaseNo: number, + testcaseId: number, ): Promise { - const response = await fetch(`http://localhost:2000/${competitionId}/${userId}/${problemId}`, { - method: 'POST', - }); + const response = await fetch( + `http://localhost:2000/${competitionId}/${userId}/${problemId}/${testcaseId}`, + { method: 'POST' }, + ); return (await response.json()) as ICoderunResponse; } @@ -76,19 +89,39 @@ export class ScoreService { competitionId: number, userId: number, problemId: number, - testcaseNo: number, + testcaseId: number, ): { result: string; stdout: string; stderr: string } { - const submissionBasePath = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/`; - const result = fs.readFileSync(`${submissionBasePath}/${problemId}.result`).toString(); - const stdout = fs.readFileSync(`${submissionBasePath}/${problemId}.stdout`).toString(); - const stderr = fs.readFileSync(`${submissionBasePath}/${problemId}.stderr`).toString(); + const submissionBaseFilename = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}.${testcaseId}`; + const [resultFilepath, stdoutFilepath, stderrFilepath] = [ + `${submissionBaseFilename}.result`, + `${submissionBaseFilename}.stdout`, + `${submissionBaseFilename}.stderr`, + ]; + if ( + !fs.existsSync(resultFilepath) || + !fs.existsSync(stdoutFilepath) || + !fs.existsSync(stderrFilepath) + ) { + throw new InternalServerErrorException( + `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`, + ); + } + + const [result, stdout, stderr] = [ + fs.readFileSync(resultFilepath).toString(), + fs.readFileSync(stdoutFilepath).toString(), + fs.readFileSync(stderrFilepath).toString(), + ]; return { result, stdout, stderr }; } - private getTestcaseAnswer(problemId: number, testcaseNo: number) { - const testcaseAnswer = fs - .readFileSync(`${process.env.TESTCASE_PATH}/${problemId}/secrets/${testcaseNo}.ans`) - .toString(); + private getTestcaseAnswer(problemId: number, testcaseId: number) { + const filepath = `${process.env.TESTCASE_PATH}/${problemId}/secrets/${testcaseId}.ans`; + if (!fs.existsSync(filepath)) + throw new InternalServerErrorException( + `경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`, + ); + const testcaseAnswer = fs.readFileSync(filepath).toString(); return testcaseAnswer; } From d1702c8768ccb705e919fa37063e85537117efe7 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 14:16:36 +0900 Subject: [PATCH 095/226] =?UTF-8?q?chore:=20=EC=95=88=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?logger=20=EC=A7=80=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.module.ts | 6 +++--- be/algo-with-me-score/src/score/services/score.consumer.ts | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 5b33591..28e33e4 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -35,11 +35,11 @@ import { ScoreModule } from './score/score.module'; }, }), BullModule.registerQueue({ - name: 'testQueue', + name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), ScoreModule, ], - controllers: [AppController], - providers: [AppService], + controllers: [], + providers: [], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 87c18a2..c841b15 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -11,8 +11,7 @@ import { ScoreService } from './score.service'; import { MessageQueueItemDto } from '../dtos/message-queue-item.dto'; import { Submission } from '../entities/submission.entity'; -// @Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) -@Processor('testQueue') +@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) export class SubmissionConsumer { constructor( @InjectRepository(Submission) private readonly submissionRepository: Repository , @@ -27,7 +26,6 @@ export class SubmissionConsumer { job.data.problemId, job.data.sessionId, ); - const logger = new Logger(); const submissionId = messageQueueItem.submissionId; const submission = await this.submissionRepository.findOneBy({ id: submissionId }); if (!submission) From 914a74f7aff69e806f0b8d4ef5c3934bcd27fdc3 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 14:31:00 +0900 Subject: [PATCH 096/226] =?UTF-8?q?chore:=20entity=20api=EA=B1=B0=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=99=80=EC=84=9C=20=EB=B3=B5=EB=B6=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/entities/competition.entity.ts | 44 +++++++++++++++++++ .../entities/competition.problem.entity.ts | 19 ++++++++ .../src/score/entities/problem.entity.ts | 4 ++ .../src/score/entities/submission.entity.ts | 8 +++- .../src/score/services/score.consumer.ts | 6 +-- 5 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 be/algo-with-me-score/src/score/entities/competition.entity.ts create mode 100644 be/algo-with-me-score/src/score/entities/competition.problem.entity.ts diff --git a/be/algo-with-me-score/src/score/entities/competition.entity.ts b/be/algo-with-me-score/src/score/entities/competition.entity.ts new file mode 100644 index 0000000..370fc82 --- /dev/null +++ b/be/algo-with-me-score/src/score/entities/competition.entity.ts @@ -0,0 +1,44 @@ +import { + Column, + CreateDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { CompetitionProblem } from './competition.problem.entity'; +import { Submission } from './submission.entity'; + +@Entity() +export class Competition { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @Column('text') + detail: string; + + @Column() + maxParticipants: number; + + @Column() + startsAt: Date; + + @Column() + endsAt: Date; + + @OneToMany(() => Submission, (submission) => submission.competition) + submissions: Submission[]; + + @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) + competitionProblems: CompetitionProblem[]; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts b/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts new file mode 100644 index 0000000..02dde8c --- /dev/null +++ b/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts @@ -0,0 +1,19 @@ +import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; + +import { Competition } from './competition.entity'; +import { Problem } from './problem.entity'; + +@Entity() +export class CompetitionProblem { + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(() => Competition, (competition) => competition.competitionProblems) + competition: Competition; + + @ManyToOne(() => Problem, (problem) => problem.competitionProblems) + problem: Problem; + + @CreateDateColumn() + createdAt: Date; +} diff --git a/be/algo-with-me-score/src/score/entities/problem.entity.ts b/be/algo-with-me-score/src/score/entities/problem.entity.ts index 1783663..6a2792f 100644 --- a/be/algo-with-me-score/src/score/entities/problem.entity.ts +++ b/be/algo-with-me-score/src/score/entities/problem.entity.ts @@ -7,6 +7,7 @@ import { UpdateDateColumn, } from 'typeorm'; +import { CompetitionProblem } from './competition.problem.entity'; import { Submission } from './submission.entity'; @Entity() @@ -35,6 +36,9 @@ export class Problem { @OneToMany(() => Submission, (submission) => submission.problem) submissions: Submission[]; + @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.problem) + competitionProblems: CompetitionProblem[]; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-score/src/score/entities/submission.entity.ts b/be/algo-with-me-score/src/score/entities/submission.entity.ts index b675d43..916ab4f 100644 --- a/be/algo-with-me-score/src/score/entities/submission.entity.ts +++ b/be/algo-with-me-score/src/score/entities/submission.entity.ts @@ -7,6 +7,7 @@ import { UpdateDateColumn, } from 'typeorm'; +import { Competition } from './competition.entity'; import { Problem } from './problem.entity'; import { RESULT } from './submission.enums'; @@ -25,12 +26,15 @@ export class Submission { }) result: string; - @Column('json', { nullable: true }) - detail: string; + @Column('json', { nullable: true, default: [] }) + detail: object[]; @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) problem: Problem; + @ManyToOne(() => Competition, (competition) => competition.submissions) + competition: Competition; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index c841b15..052cd22 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -32,7 +32,8 @@ export class SubmissionConsumer { throw new InternalServerErrorException( `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`, ); - const { competitionId, userId, problemId } = this.getIds(submission); + const problemId = messageQueueItem.problemId; + const { competitionId, userId } = this.getIds(submission); this.filesystemService.writeSubmittedCode(competitionId, userId, problemId); @@ -46,10 +47,9 @@ export class SubmissionConsumer { } private getIds(submission: Submission) { - // TODO: submission으로부터 competitionId, problemId, userId를 받을 수 있어야 함 const competitionId = 1; const userId = 1; - const problemId = 1; + const problemId = submission.problem.id; return { competitionId, userId, problemId }; } From 0e9ef414598f03cbb2da51a8a8d083d9936a118a Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 14:38:41 +0900 Subject: [PATCH 097/226] =?UTF-8?q?chore:=20typeorm=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.module.ts | 8 +++++--- be/algo-with-me-score/src/app.service.ts | 2 +- be/algo-with-me-score/src/score/score.module.ts | 5 ++++- .../src/score/services/score.consumer.ts | 14 +++++--------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 28e33e4..233b4c6 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -7,6 +7,8 @@ import * as process from 'node:process'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { Competition } from './score/entities/competition.entity'; +import { CompetitionProblem } from './score/entities/competition.problem.entity'; import { Problem } from './score/entities/problem.entity'; import { Submission } from './score/entities/submission.entity'; import { ScoreModule } from './score/score.module'; @@ -25,7 +27,7 @@ import { ScoreModule } from './score/score.module'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: false, - entities: [Submission, Problem], + entities: [Competition, CompetitionProblem, Submission, Problem], }), BullModule.forRoot({ redis: { @@ -39,7 +41,7 @@ import { ScoreModule } from './score/score.module'; }), ScoreModule, ], - controllers: [], - providers: [], + controllers: [AppController], + providers: [AppService], }) export class AppModule {} diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts index 7a705c3..e67dd89 100644 --- a/be/algo-with-me-score/src/app.service.ts +++ b/be/algo-with-me-score/src/app.service.ts @@ -6,7 +6,7 @@ import { MessageQueueItemDto } from './score/dtos/message-queue-item.dto'; @Injectable() export class AppService { - constructor(@InjectQueue('testQueue') private testQueue: Queue) {} + constructor(@InjectQueue('submission') private testQueue: Queue) {} async addMessageQueue(item: MessageQueueItemDto) { return await this.testQueue.add('score', item); diff --git a/be/algo-with-me-score/src/score/score.module.ts b/be/algo-with-me-score/src/score/score.module.ts index c827154..1785dd9 100644 --- a/be/algo-with-me-score/src/score/score.module.ts +++ b/be/algo-with-me-score/src/score/score.module.ts @@ -2,6 +2,9 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { Competition } from './entities/competition.entity'; +import { CompetitionProblem } from './entities/competition.problem.entity'; +import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; import { FilesystemService } from './services/filesystem.service'; import { SubmissionConsumer } from './services/score.consumer'; @@ -9,7 +12,7 @@ import { ScoreService } from './services/score.service'; @Module({ imports: [ - TypeOrmModule.forFeature([Submission]), + TypeOrmModule.forFeature([Competition, CompetitionProblem, Submission, Problem]), BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 052cd22..7f8f66b 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -1,5 +1,5 @@ import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; -import { InternalServerErrorException, Logger } from '@nestjs/common'; +import { InternalServerErrorException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Job } from 'bull'; import { Repository } from 'typeorm'; @@ -32,8 +32,11 @@ export class SubmissionConsumer { throw new InternalServerErrorException( `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`, ); + const problemId = messageQueueItem.problemId; - const { competitionId, userId } = this.getIds(submission); + const competitionId = submission.competition.id; + // TODO: userId 가져오기 + const userId = 1; this.filesystemService.writeSubmittedCode(competitionId, userId, problemId); @@ -46,13 +49,6 @@ export class SubmissionConsumer { ); } - private getIds(submission: Submission) { - const competitionId = 1; - const userId = 1; - const problemId = submission.problem.id; - return { competitionId, userId, problemId }; - } - @OnQueueCompleted() async onCompleted(job: Job) { await job.remove(); From 46a5938788d0141b8dc6f9529059aaed4fd90cdc Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 15:09:24 +0900 Subject: [PATCH 098/226] =?UTF-8?q?fix:=20submission=20entity=EB=A5=BC=20?= =?UTF-8?q?=EA=B3=A0=EC=B9=A8=20(https://stackoverflow.com/questions/69763?= =?UTF-8?q?283/why-wont-this-simple-typeorm-query-return-my-manytoone-prop?= =?UTF-8?q?erty-with-postgresql)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.controller.ts | 10 ++------- .../src/score/dtos/message-queue-item.dto.ts | 4 +--- .../src/score/entities/submission.entity.ts | 9 ++++++++ .../src/score/services/score.consumer.ts | 22 +++++++++---------- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/be/algo-with-me-score/src/app.controller.ts b/be/algo-with-me-score/src/app.controller.ts index ec3105a..4744040 100644 --- a/be/algo-with-me-score/src/app.controller.ts +++ b/be/algo-with-me-score/src/app.controller.ts @@ -8,13 +8,7 @@ export class AppController { constructor(private readonly appService: AppService) {} @Post() - addMessage( - @Body('submissionId') submissionId: number, - @Body('problemId') problemId: number, - @Body('sessionId') sessionId: string, - ) { - return this.appService.addMessageQueue( - new MessageQueueItemDto(submissionId, problemId, sessionId), - ); + addMessage(@Body('submissionId') submissionId: number, @Body('sessionId') sessionId: string) { + return this.appService.addMessageQueue(new MessageQueueItemDto(submissionId, sessionId)); } } diff --git a/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts b/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts index 02ccbee..f866735 100644 --- a/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts +++ b/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts @@ -1,11 +1,9 @@ export class MessageQueueItemDto { - constructor(submissionId: number, problemId: number, sessionId: string) { + constructor(submissionId: number, sessionId: string) { this.submissionId = submissionId; - this.problemId = problemId; this.sessionId = sessionId; } submissionId: number; - problemId: number; sessionId: string; } diff --git a/be/algo-with-me-score/src/score/entities/submission.entity.ts b/be/algo-with-me-score/src/score/entities/submission.entity.ts index 916ab4f..0e3682f 100644 --- a/be/algo-with-me-score/src/score/entities/submission.entity.ts +++ b/be/algo-with-me-score/src/score/entities/submission.entity.ts @@ -2,6 +2,7 @@ import { Column, CreateDateColumn, Entity, + JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, @@ -29,10 +30,18 @@ export class Submission { @Column('json', { nullable: true, default: [] }) detail: object[]; + @Column() + problemId: number; + @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) + @JoinColumn({ name: 'problemId', referencedColumnName: 'id' }) problem: Problem; + @Column() + competitionId: number; + @ManyToOne(() => Competition, (competition) => competition.submissions) + @JoinColumn({ name: 'competitionId', referencedColumnName: 'id' }) competition: Competition; @CreateDateColumn() diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 7f8f66b..57b1a48 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -1,5 +1,5 @@ import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; -import { InternalServerErrorException } from '@nestjs/common'; +import { InternalServerErrorException, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Job } from 'bull'; import { Repository } from 'typeorm'; @@ -11,7 +11,7 @@ import { ScoreService } from './score.service'; import { MessageQueueItemDto } from '../dtos/message-queue-item.dto'; import { Submission } from '../entities/submission.entity'; -@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +@Processor('submission') export class SubmissionConsumer { constructor( @InjectRepository(Submission) private readonly submissionRepository: Repository , @@ -21,22 +21,22 @@ export class SubmissionConsumer { @Process('score') async getMessageQueue(job: Job) { - const messageQueueItem = new MessageQueueItemDto( - job.data.submissionId, - job.data.problemId, - job.data.sessionId, - ); + const logger = new Logger(); + const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.sessionId); + logger.debug(JSON.stringify(messageQueueItem)); const submissionId = messageQueueItem.submissionId; - const submission = await this.submissionRepository.findOneBy({ id: submissionId }); + const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); + logger.debug(JSON.stringify(submission)); if (!submission) throw new InternalServerErrorException( `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`, ); - - const problemId = messageQueueItem.problemId; - const competitionId = submission.competition.id; + logger.debug('hey'); + const problemId = submission.problemId; + const competitionId = submission.competitionId; // TODO: userId 가져오기 const userId = 1; + logger.debug(JSON.stringify({ problemId, competitionId, userId })); this.filesystemService.writeSubmittedCode(competitionId, userId, problemId); From bde6317a9b2e5312d78bbe82f093ab7b4a48019f Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 15:40:51 +0900 Subject: [PATCH 099/226] =?UTF-8?q?fix:=20process.env=20=EB=8C=80=EC=8B=A0?= =?UTF-8?q?=20configService=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=94=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/filesystem.service.ts | 32 +++++++++++++------ .../src/score/services/score.consumer.ts | 12 ++++--- .../src/score/services/score.service.ts | 26 +++++++++------ 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 22c806d..9649b91 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -1,22 +1,34 @@ import { InternalServerErrorException, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import fs from 'node:fs'; -import process from 'process'; import { Problem } from '../entities/problem.entity'; export class FilesystemService { - constructor(private readonly problemRepository: Repository ) {} - - public writeSubmittedCode(competitionId: number, userId: number, problemId: number) { - // TODO: code와 프레임코드 조인해서 가져오기 - const code = ''; - const frameCode = ''; - const mergedCode = this.getMergedCode(code, frameCode); - const filepath = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}`; - if (!fs.existsSync(filepath)) + constructor( + @InjectRepository(Problem) private readonly problemRepository: Repository , + private readonly configService: ConfigService, + ) {} + + async writeSubmittedCode(code: string, competitionId: number, userId: number, problemId: number) { + const logger = new Logger(); + const problem: Problem = await this.problemRepository.findOneBy({ id: problemId }); + const mergedCode = this.getMergedCode(code, problem.frameCode); + + const submissionPath = this.configService.get ('SUBMISSION_PATH'); + logger.debug(JSON.stringify(submissionPath)); + const filepath = `${submissionPath}/${competitionId}/${userId}/${problemId}`; + logger.debug(JSON.stringify(filepath)); + + if (!fs.existsSync(filepath)) { + logger.debug('어루'); throw new InternalServerErrorException(`경로 ${filepath}가 없습니다`); + } + logger.debug('?'); + fs.writeFileSync(filepath, mergedCode); } diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 57b1a48..7824277 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -23,22 +23,26 @@ export class SubmissionConsumer { async getMessageQueue(job: Job) { const logger = new Logger(); const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.sessionId); - logger.debug(JSON.stringify(messageQueueItem)); const submissionId = messageQueueItem.submissionId; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); - logger.debug(JSON.stringify(submission)); if (!submission) throw new InternalServerErrorException( `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`, ); - logger.debug('hey'); const problemId = submission.problemId; const competitionId = submission.competitionId; // TODO: userId 가져오기 const userId = 1; logger.debug(JSON.stringify({ problemId, competitionId, userId })); - this.filesystemService.writeSubmittedCode(competitionId, userId, problemId); + await this.filesystemService.writeSubmittedCode( + submission.code, + competitionId, + userId, + problemId, + ); + + logger.debug('씀'); await this.scoreService.scoreAllAndSendResult( submission, diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 0ad68f0..156df48 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -1,4 +1,5 @@ import { InternalServerErrorException, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import fs from 'node:fs'; import process from 'process'; @@ -9,6 +10,8 @@ import { RESULT } from '../entities/submission.enums'; import ICoderunResponse from '../interfaces/coderun-response.interface'; export class ScoreService { + constructor(private readonly configService: ConfigService) {} + public async scoreAllAndSendResult( submission: Submission, submissionId: number, @@ -62,14 +65,15 @@ export class ScoreService { private async sendScoreResult(scoreResult: ScoreResultDto) { const logger = new Logger(); - logger.debug(JSON.stringify(scoreResult)); - await fetch( - `http://${process.env.API_SERVER_HOST}:${process.env.API_SERVER_PORT}/competitions/scores`, - { - method: 'POST', - body: JSON.stringify(scoreResult), - }, - ); + logger.debug('sendScoreResult: ', JSON.stringify(scoreResult)); + const [apiServerHost, apiServerPort] = [ + this.configService.get ('API_SERVER_HOST'), + this.configService.get ('API_SERVER_HOST'), + ]; + await fetch(`http://${apiServerHost}:${apiServerPort}/competitions/scores`, { + method: 'POST', + body: JSON.stringify(scoreResult), + }); } private async runCode( @@ -91,7 +95,8 @@ export class ScoreService { problemId: number, testcaseId: number, ): { result: string; stdout: string; stderr: string } { - const submissionBaseFilename = `${process.env.SUBMISSION_PATH}/${competitionId}/${userId}/${problemId}.${testcaseId}`; + const submissionPath = this.configService.get ('SUBMISSION_PATH'); + const submissionBaseFilename = `${submissionPath}/${competitionId}/${userId}/${problemId}.${testcaseId}`; const [resultFilepath, stdoutFilepath, stderrFilepath] = [ `${submissionBaseFilename}.result`, `${submissionBaseFilename}.stdout`, @@ -116,7 +121,8 @@ export class ScoreService { } private getTestcaseAnswer(problemId: number, testcaseId: number) { - const filepath = `${process.env.TESTCASE_PATH}/${problemId}/secrets/${testcaseId}.ans`; + const testcasePath = this.configService.get ('TESTCASE_PATH'); + const filepath = `${testcasePath}/${problemId}/secrets/${testcaseId}.ans`; if (!fs.existsSync(filepath)) throw new InternalServerErrorException( `경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`, From c12cc57cc3c56c1d25ea5028b6f90ce3d50bb269 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 15:52:09 +0900 Subject: [PATCH 100/226] =?UTF-8?q?fix:=20file=20path=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/filesystem.service.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 9649b91..9688ed7 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -3,7 +3,8 @@ import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import fs from 'node:fs'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { Problem } from '../entities/problem.entity'; @@ -14,22 +15,19 @@ export class FilesystemService { ) {} async writeSubmittedCode(code: string, competitionId: number, userId: number, problemId: number) { - const logger = new Logger(); const problem: Problem = await this.problemRepository.findOneBy({ id: problemId }); const mergedCode = this.getMergedCode(code, problem.frameCode); const submissionPath = this.configService.get ('SUBMISSION_PATH'); - logger.debug(JSON.stringify(submissionPath)); - const filepath = `${submissionPath}/${competitionId}/${userId}/${problemId}`; - logger.debug(JSON.stringify(filepath)); + const baseDirectory = `${submissionPath}/${competitionId}/${userId}/`; - if (!fs.existsSync(filepath)) { - logger.debug('어루'); - throw new InternalServerErrorException(`경로 ${filepath}가 없습니다`); + if (!fs.existsSync(baseDirectory)) { + const logger = new Logger(); + logger.error(`파일시스템에 ${baseDirectory} 경로가 존재하지 않습니다`); + throw new InternalServerErrorException(`경로 ${baseDirectory}가 없습니다`); } - logger.debug('?'); - fs.writeFileSync(filepath, mergedCode); + fs.writeFileSync(path.join(baseDirectory, `${problemId}.js`), mergedCode); } private getMergedCode(code: string, frameCode: string) { From 9c7f7e27802e0133e3fb082fca1350a7677808fb Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 16:02:50 +0900 Subject: [PATCH 101/226] =?UTF-8?q?fix:=20=EB=84=A4=ED=8A=B8=EC=9B=8C?= =?UTF-8?q?=ED=81=AC=20=EC=A0=84=EC=86=A1=20=EC=AA=BD=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/filesystem.service.ts | 6 +-- .../src/score/services/score.service.ts | 49 ++++++++++++------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 9688ed7..02b5559 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -22,9 +22,9 @@ export class FilesystemService { const baseDirectory = `${submissionPath}/${competitionId}/${userId}/`; if (!fs.existsSync(baseDirectory)) { - const logger = new Logger(); - logger.error(`파일시스템에 ${baseDirectory} 경로가 존재하지 않습니다`); - throw new InternalServerErrorException(`경로 ${baseDirectory}가 없습니다`); + const message = `파일시스템에 ${baseDirectory} 경로가 존재하지 않습니다`; + new Logger().error(message); + throw new InternalServerErrorException(message); } fs.writeFileSync(path.join(baseDirectory, `${problemId}.js`), mergedCode); diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 156df48..2e6da08 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -64,16 +64,21 @@ export class ScoreService { } private async sendScoreResult(scoreResult: ScoreResultDto) { - const logger = new Logger(); - logger.debug('sendScoreResult: ', JSON.stringify(scoreResult)); const [apiServerHost, apiServerPort] = [ this.configService.get ('API_SERVER_HOST'), - this.configService.get ('API_SERVER_HOST'), + this.configService.get ('API_SERVER_PORT'), ]; - await fetch(`http://${apiServerHost}:${apiServerPort}/competitions/scores`, { - method: 'POST', - body: JSON.stringify(scoreResult), - }); + const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; + try { + await fetch(url, { + method: 'POST', + body: JSON.stringify(scoreResult), + }); + } catch (error) { + const message = `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url})`; + new Logger().error(message); + throw new InternalServerErrorException(message); + } } private async runCode( @@ -82,11 +87,15 @@ export class ScoreService { problemId: number, testcaseId: number, ): Promise { - const response = await fetch( - `http://localhost:2000/${competitionId}/${userId}/${problemId}/${testcaseId}`, - { method: 'POST' }, - ); - return (await response.json()) as ICoderunResponse; + const url = `http://localhost:2000/${competitionId}/${userId}/${problemId}/${testcaseId}`; + try { + const response = await fetch(url, { method: 'POST' }); + return (await response.json()) as ICoderunResponse; + } catch (error) { + const message = `도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url})`; + new Logger().error(message); + throw new InternalServerErrorException(message); + } } private getCodeRunOutputs( @@ -107,9 +116,9 @@ export class ScoreService { !fs.existsSync(stdoutFilepath) || !fs.existsSync(stderrFilepath) ) { - throw new InternalServerErrorException( - `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`, - ); + const message = `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`; + new Logger().error(message); + throw new InternalServerErrorException(message); } const [result, stdout, stderr] = [ @@ -123,10 +132,12 @@ export class ScoreService { private getTestcaseAnswer(problemId: number, testcaseId: number) { const testcasePath = this.configService.get ('TESTCASE_PATH'); const filepath = `${testcasePath}/${problemId}/secrets/${testcaseId}.ans`; - if (!fs.existsSync(filepath)) - throw new InternalServerErrorException( - `경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`, - ); + if (!fs.existsSync(filepath)) { + const message = `경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`; + new Logger().error(message); + throw new InternalServerErrorException(message); + } + const testcaseAnswer = fs.readFileSync(filepath).toString(); return testcaseAnswer; } From 5cf460aa10f1748e84c38f953f9a08af1a43cfb5 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 16:38:37 +0900 Subject: [PATCH 102/226] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/filesystem.service.ts | 1 + .../src/score/services/score.consumer.ts | 20 +++++++++---------- .../src/score/services/score.service.ts | 20 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 02b5559..4194a8f 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -31,6 +31,7 @@ export class FilesystemService { } private getMergedCode(code: string, frameCode: string) { + // TODO: 프레임코드 const mergedCode = code + frameCode; return mergedCode; } diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 7824277..5fd47f5 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -4,36 +4,36 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Job } from 'bull'; import { Repository } from 'typeorm'; -import * as process from 'process'; - import { FilesystemService } from './filesystem.service'; import { ScoreService } from './score.service'; import { MessageQueueItemDto } from '../dtos/message-queue-item.dto'; +import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; @Processor('submission') export class SubmissionConsumer { constructor( @InjectRepository(Submission) private readonly submissionRepository: Repository , + @InjectRepository(Problem) private readonly problemRepository: Repository , private readonly filesystemService: FilesystemService, private readonly scoreService: ScoreService, ) {} @Process('score') async getMessageQueue(job: Job) { - const logger = new Logger(); const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.sessionId); const submissionId = messageQueueItem.submissionId; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); - if (!submission) - throw new InternalServerErrorException( - `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`, - ); + if (!submission) { + const message = `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`; + new Logger().error(message); + throw new InternalServerErrorException(message); + } + const problemId = submission.problemId; const competitionId = submission.competitionId; // TODO: userId 가져오기 const userId = 1; - logger.debug(JSON.stringify({ problemId, competitionId, userId })); await this.filesystemService.writeSubmittedCode( submission.code, @@ -42,10 +42,10 @@ export class SubmissionConsumer { problemId, ); - logger.debug('씀'); - + const problem: Problem = await this.problemRepository.findOneBy({ id: problemId }); await this.scoreService.scoreAllAndSendResult( submission, + problem.testcaseNum, submissionId, competitionId, userId, diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 2e6da08..0618087 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -1,8 +1,6 @@ import { InternalServerErrorException, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import fs from 'node:fs'; -import process from 'process'; +import * as fs from 'node:fs'; import { ScoreResultDto } from '../dtos/score-result.dto'; import { Submission } from '../entities/submission.entity'; @@ -10,16 +8,17 @@ import { RESULT } from '../entities/submission.enums'; import ICoderunResponse from '../interfaces/coderun-response.interface'; export class ScoreService { - constructor(private readonly configService: ConfigService) {} + constructor() {} public async scoreAllAndSendResult( submission: Submission, + testcaseNum: number, submissionId: number, competitionId: number, userId: number, problemId: number, ) { - for (let testcaseId = 1; testcaseId <= submission.problem.testcaseNum; testcaseId++) { + for (let testcaseId = 1; testcaseId <= testcaseNum; testcaseId++) { await this.scoreOneTestcaseAndSendResult( submissionId, competitionId, @@ -65,8 +64,8 @@ export class ScoreService { private async sendScoreResult(scoreResult: ScoreResultDto) { const [apiServerHost, apiServerPort] = [ - this.configService.get ('API_SERVER_HOST'), - this.configService.get ('API_SERVER_PORT'), + process.env.API_SERVER_HOST, + process.env.API_SERVER_PORT, ]; const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; try { @@ -104,7 +103,7 @@ export class ScoreService { problemId: number, testcaseId: number, ): { result: string; stdout: string; stderr: string } { - const submissionPath = this.configService.get ('SUBMISSION_PATH'); + const submissionPath = process.env.SUBMISSION_PATH; const submissionBaseFilename = `${submissionPath}/${competitionId}/${userId}/${problemId}.${testcaseId}`; const [resultFilepath, stdoutFilepath, stderrFilepath] = [ `${submissionBaseFilename}.result`, @@ -130,7 +129,7 @@ export class ScoreService { } private getTestcaseAnswer(problemId: number, testcaseId: number) { - const testcasePath = this.configService.get ('TESTCASE_PATH'); + const testcasePath = process.env.TESTCASE_PATH; const filepath = `${testcasePath}/${problemId}/secrets/${testcaseId}.ans`; if (!fs.existsSync(filepath)) { const message = `경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`; @@ -138,8 +137,7 @@ export class ScoreService { throw new InternalServerErrorException(message); } - const testcaseAnswer = fs.readFileSync(filepath).toString(); - return testcaseAnswer; + return fs.readFileSync(filepath).toString(); } private judge( From 0c24a4e85735037ec855a0f69c09886967ffc032 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 18:16:07 +0900 Subject: [PATCH 103/226] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20sessionId=EB=9D=BC=EA=B3=A0=20=EC=9E=98?= =?UTF-8?q?=EB=AA=BB=20=EC=A0=81=EC=9D=80=20=EA=B2=83=EC=9D=84=20socketId?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.controller.ts | 4 ++-- .../src/score/dtos/message-queue-item.dto.ts | 6 +++--- be/algo-with-me-score/src/score/services/score.consumer.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/be/algo-with-me-score/src/app.controller.ts b/be/algo-with-me-score/src/app.controller.ts index 4744040..b38e79b 100644 --- a/be/algo-with-me-score/src/app.controller.ts +++ b/be/algo-with-me-score/src/app.controller.ts @@ -8,7 +8,7 @@ export class AppController { constructor(private readonly appService: AppService) {} @Post() - addMessage(@Body('submissionId') submissionId: number, @Body('sessionId') sessionId: string) { - return this.appService.addMessageQueue(new MessageQueueItemDto(submissionId, sessionId)); + addMessage(@Body('submissionId') submissionId: number, @Body('socketId') socketId: string) { + return this.appService.addMessageQueue(new MessageQueueItemDto(submissionId, socketId)); } } diff --git a/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts b/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts index f866735..5a4f570 100644 --- a/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts +++ b/be/algo-with-me-score/src/score/dtos/message-queue-item.dto.ts @@ -1,9 +1,9 @@ export class MessageQueueItemDto { - constructor(submissionId: number, sessionId: string) { + constructor(submissionId: number, socketId: string) { this.submissionId = submissionId; - this.sessionId = sessionId; + this.socketId = socketId; } submissionId: number; - sessionId: string; + socketId: string; } diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 5fd47f5..ddfe955 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -21,7 +21,7 @@ export class SubmissionConsumer { @Process('score') async getMessageQueue(job: Job) { - const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.sessionId); + const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.socketId); const submissionId = messageQueueItem.submissionId; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); if (!submission) { From fbd6316df903a009c2874ed94049a777f4198a4f Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 18:17:37 +0900 Subject: [PATCH 104/226] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20redis=20message=20queue=20name=20=EC=88=A8?= =?UTF-8?q?=EA=B8=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.service.ts | 2 +- be/algo-with-me-score/src/score/services/score.consumer.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts index e67dd89..191a952 100644 --- a/be/algo-with-me-score/src/app.service.ts +++ b/be/algo-with-me-score/src/app.service.ts @@ -6,7 +6,7 @@ import { MessageQueueItemDto } from './score/dtos/message-queue-item.dto'; @Injectable() export class AppService { - constructor(@InjectQueue('submission') private testQueue: Queue) {} + constructor(@InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private testQueue: Queue) {} async addMessageQueue(item: MessageQueueItemDto) { return await this.testQueue.add('score', item); diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index ddfe955..c1f607e 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -4,13 +4,15 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Job } from 'bull'; import { Repository } from 'typeorm'; +import * as process from 'process'; + import { FilesystemService } from './filesystem.service'; import { ScoreService } from './score.service'; import { MessageQueueItemDto } from '../dtos/message-queue-item.dto'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; -@Processor('submission') +@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) export class SubmissionConsumer { constructor( @InjectRepository(Submission) private readonly submissionRepository: Repository , From 6ab34e739837776ee80e8e8fd0babf4ec23ce5cc Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 18:19:42 +0900 Subject: [PATCH 105/226] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20exception=20=EB=8D=98=EC=A7=88=20=EB=95=8C=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=82=B4=EB=B6=80=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EA=B0=90=EC=B6=94=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=97=86=EC=95=A0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/filesystem.service.ts | 5 ++--- .../src/score/services/score.consumer.ts | 5 ++--- .../src/score/services/score.service.ts | 22 +++++++++---------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 4194a8f..aec97ab 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -22,9 +22,8 @@ export class FilesystemService { const baseDirectory = `${submissionPath}/${competitionId}/${userId}/`; if (!fs.existsSync(baseDirectory)) { - const message = `파일시스템에 ${baseDirectory} 경로가 존재하지 않습니다`; - new Logger().error(message); - throw new InternalServerErrorException(message); + new Logger().error(`파일시스템에 ${baseDirectory} 경로가 존재하지 않습니다`); + throw new InternalServerErrorException(); } fs.writeFileSync(path.join(baseDirectory, `${problemId}.js`), mergedCode); diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index c1f607e..b22349f 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -27,9 +27,8 @@ export class SubmissionConsumer { const submissionId = messageQueueItem.submissionId; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); if (!submission) { - const message = `제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`; - new Logger().error(message); - throw new InternalServerErrorException(message); + new Logger().error(`제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`); + throw new InternalServerErrorException(); } const problemId = submission.problemId; diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 0618087..aed5fbc 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -74,9 +74,8 @@ export class ScoreService { body: JSON.stringify(scoreResult), }); } catch (error) { - const message = `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url})`; - new Logger().error(message); - throw new InternalServerErrorException(message); + new Logger().error(`API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url})`); + throw new InternalServerErrorException(); } } @@ -91,9 +90,8 @@ export class ScoreService { const response = await fetch(url, { method: 'POST' }); return (await response.json()) as ICoderunResponse; } catch (error) { - const message = `도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url})`; - new Logger().error(message); - throw new InternalServerErrorException(message); + new Logger().error(`도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url})`); + throw new InternalServerErrorException(); } } @@ -115,9 +113,10 @@ export class ScoreService { !fs.existsSync(stdoutFilepath) || !fs.existsSync(stderrFilepath) ) { - const message = `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`; - new Logger().error(message); - throw new InternalServerErrorException(message); + new Logger().error( + `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`, + ); + throw new InternalServerErrorException(); } const [result, stdout, stderr] = [ @@ -132,9 +131,8 @@ export class ScoreService { const testcasePath = process.env.TESTCASE_PATH; const filepath = `${testcasePath}/${problemId}/secrets/${testcaseId}.ans`; if (!fs.existsSync(filepath)) { - const message = `경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`; - new Logger().error(message); - throw new InternalServerErrorException(message); + new Logger().error(`경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`); + throw new InternalServerErrorException(); } return fs.readFileSync(filepath).toString(); From 120498b55da32c3fea7a34985785d9666df79f7f Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 18:23:25 +0900 Subject: [PATCH 106/226] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20socketId=20=EC=A0=84=EB=8B=AC,=20responseDTO=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD(competitionId,=20userId,=20problemId=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/dtos/score-result.dto.ts | 12 +++--------- .../src/score/services/score.consumer.ts | 3 ++- .../src/score/services/score.service.ts | 16 ++++------------ 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts index 6be0685..3733d4e 100644 --- a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts +++ b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts @@ -3,10 +3,8 @@ import { RESULT } from '../entities/submission.enums'; export class ScoreResultDto { constructor( submissionId: number, - competitionId: number, - userId: number, - problemId: number, testcaseId: number, + socketId: string, result: keyof typeof RESULT, stdout: string, stderr: string, @@ -14,10 +12,8 @@ export class ScoreResultDto { memoryUsage: number, ) { this.submissionId = submissionId; - this.competitionId = competitionId; - this.userId = userId; - this.problemId = problemId; this.testcaseId = testcaseId; + this.socketId = socketId; this.result = result; this.stdout = stdout; this.stderr = stderr; @@ -26,10 +22,8 @@ export class ScoreResultDto { } submissionId: number; - competitionId: number; - userId: number; - problemId: number; testcaseId: number; + socketId: string; result: keyof typeof RESULT; stdout: string; stderr: string; diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index b22349f..82bc782 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -24,7 +24,7 @@ export class SubmissionConsumer { @Process('score') async getMessageQueue(job: Job) { const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.socketId); - const submissionId = messageQueueItem.submissionId; + const { socketId, submissionId } = messageQueueItem; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); if (!submission) { new Logger().error(`제출 id ${submissionId}에 해당하는 제출 정보를 찾을 수 없습니다`); @@ -51,6 +51,7 @@ export class SubmissionConsumer { competitionId, userId, problemId, + socketId, ); } diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index aed5fbc..405cc78 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -17,6 +17,7 @@ export class ScoreService { competitionId: number, userId: number, problemId: number, + socketId: string, ) { for (let testcaseId = 1; testcaseId <= testcaseNum; testcaseId++) { await this.scoreOneTestcaseAndSendResult( @@ -25,6 +26,7 @@ export class ScoreService { userId, problemId, testcaseId, + socketId, ); } } @@ -35,6 +37,7 @@ export class ScoreService { userId: number, problemId: number, testcaseId: number, + socketId: string, ) { const codeRunResponse = await this.runCode(competitionId, userId, problemId, testcaseId); @@ -47,18 +50,7 @@ export class ScoreService { const judgeResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); await this.sendScoreResult( - new ScoreResultDto( - submissionId, - competitionId, - userId, - problemId, - testcaseId, - judgeResult, - stdout, - stderr, - 0, - 0, - ), + new ScoreResultDto(submissionId, testcaseId, socketId, judgeResult, stdout, stderr, 0, 0), ); } From f93794068d2a3e96bb1ffa754bb28380e2b9b2d7 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 22 Nov 2023 21:39:27 +0900 Subject: [PATCH 107/226] =?UTF-8?q?fix:=20=EC=9C=A0=EC=A0=80-=EC=A0=9C?= =?UTF-8?q?=EC=B6=9C=20=EA=B4=80=EA=B3=84,=20not=20null=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/entities/submission.entity.ts | 7 ++++++- be/algo-with-me-api/src/user/entities/user.entity.ts | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts index edf22db..0ae6a3d 100644 --- a/be/algo-with-me-api/src/competition/entities/submission.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -11,6 +11,8 @@ import { Competition } from './competition.entity'; import { Problem } from './problem.entity'; import { RESULT } from '../competition.enums'; +import { User } from '@src/user/entities/user.entity'; + @Entity() export class Submission { @PrimaryGeneratedColumn() @@ -38,9 +40,12 @@ export class Submission { @Column() competitionId: number; - @ManyToOne(() => Competition, (competition) => competition.submissions, {nullable: false}) + @ManyToOne(() => Competition, (competition) => competition.submissions, { nullable: false }) competition: Competition; + @ManyToOne(() => User, (user) => user.submissions, { nullable: false }) + user: User; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index 16f3db4..5fc62fb 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -9,6 +9,7 @@ import { } from 'typeorm'; import { CompetitionParticipant } from '@src/competition/entities/competition.participant.entity'; +import { Submission } from '@src/competition/entities/submission.entity'; @Entity() export class User { @@ -25,6 +26,9 @@ export class User { @OneToMany(() => CompetitionParticipant, (competitionParticipant) => competitionParticipant.user) competitionParticipant: CompetitionParticipant[]; + @OneToMany(() => Submission, (submission) => submission.user, { nullable: false }) + submissions: Submission; + @CreateDateColumn() createdAt: Date; From 938aff1632d8a9cc4a21137691596514c5cfa2c7 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:00:44 +0900 Subject: [PATCH 108/226] =?UTF-8?q?fix:=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EC=9E=90=EB=8F=99=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=BC=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 3 ++- .../src/competition/entities/competition.entity.ts | 9 +++++++++ .../competition/entities/competition.problem.entity.ts | 6 ++++-- .../src/competition/entities/submission.entity.ts | 3 +++ be/algo-with-me-api/src/user/entities/user.entity.ts | 8 ++++++-- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index db87035..a404f25 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -27,7 +27,8 @@ import { Competition } from '@src/competition/entities/competition.entity'; username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, - synchronize: true, + synchronize: true, // db 스키마(table) 자동 생성 + migrationsRun: true, // db 변경시 바로 적용 entities: [ Problem, Submission, diff --git a/be/algo-with-me-api/src/competition/entities/competition.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.entity.ts index 7105e76..8f6796f 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.entity.ts @@ -2,6 +2,7 @@ import { Column, CreateDateColumn, Entity, + ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, @@ -11,6 +12,8 @@ import { CompetitionParticipant } from './competition.participant.entity'; import { CompetitionProblem } from './competition.problem.entity'; import { Submission } from './submission.entity'; +import { User } from '@src/user/entities/user.entity'; + @Entity() export class Competition { @PrimaryGeneratedColumn() @@ -43,6 +46,12 @@ export class Competition { ) competitionParticipants: CompetitionParticipant[]; + @Column() + userId: number; + + @ManyToOne(() => User, (user) => user.competitions, { nullable: false }) + user: User; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts index 1aad3cf..1c1399f 100644 --- a/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/competition.problem.entity.ts @@ -11,13 +11,15 @@ export class CompetitionProblem { @Column() competitionId: number; - @ManyToOne(() => Competition, (competition) => competition.competitionProblems) + @ManyToOne(() => Competition, (competition) => competition.competitionProblems, { + nullable: false, + }) competition: Competition; @Column() problemId: number; - @ManyToOne(() => Problem, (problem) => problem.competitionProblems) + @ManyToOne(() => Problem, (problem) => problem.competitionProblems, { nullable: false }) problem: Problem; @CreateDateColumn() diff --git a/be/algo-with-me-api/src/competition/entities/submission.entity.ts b/be/algo-with-me-api/src/competition/entities/submission.entity.ts index 0ae6a3d..37791f4 100644 --- a/be/algo-with-me-api/src/competition/entities/submission.entity.ts +++ b/be/algo-with-me-api/src/competition/entities/submission.entity.ts @@ -43,6 +43,9 @@ export class Submission { @ManyToOne(() => Competition, (competition) => competition.submissions, { nullable: false }) competition: Competition; + @Column() + userId: number; + @ManyToOne(() => User, (user) => user.submissions, { nullable: false }) user: User; diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index 5fc62fb..b648740 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -8,6 +8,7 @@ import { UpdateDateColumn, } from 'typeorm'; +import { Competition } from '@src/competition/entities/competition.entity'; import { CompetitionParticipant } from '@src/competition/entities/competition.participant.entity'; import { Submission } from '@src/competition/entities/submission.entity'; @@ -26,8 +27,11 @@ export class User { @OneToMany(() => CompetitionParticipant, (competitionParticipant) => competitionParticipant.user) competitionParticipant: CompetitionParticipant[]; - @OneToMany(() => Submission, (submission) => submission.user, { nullable: false }) - submissions: Submission; + @OneToMany(() => Submission, (submission) => submission.user) + submissions: Submission[]; + + @OneToMany(() => Competition, (competition) => competition.user) + competitions: Competition[]; @CreateDateColumn() createdAt: Date; From f5fe66d624dff9740799a0543a80d8765b638b7d Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:48:38 +0900 Subject: [PATCH 109/226] =?UTF-8?q?fix:=20dto=20=EC=88=98=EC=A0=95,=20auth?= =?UTF-8?q?guard=20=EC=9D=B4=ED=9B=84=20=EC=9C=A0=EC=A0=80=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=AA=85=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EB=8D=B0=EC=BD=94=EB=A0=88=EC=9D=B4=ED=84=B0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/auth/auth.strategy.ts | 2 +- .../src/auth/controllers/auth.controller.ts | 3 ++- .../controllers/competition.controller.ts | 2 ++ .../competition/dto/create-competition.dto.ts | 24 +++++++++++++++---- .../src/user/decorators/user.decorators.ts | 9 +++++++ .../src/user/dto/user.response.dto.ts | 1 + .../src/user/entities/user.entity.ts | 3 --- 7 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 be/algo-with-me-api/src/user/decorators/user.decorators.ts diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts index 8309036..561eb77 100644 --- a/be/algo-with-me-api/src/auth/auth.strategy.ts +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -43,6 +43,6 @@ export class JWTStrategy extends PassportStrategy(PassportJwtStrategy) { } async validate(content: any) { - return { email: content.sub, nickname: content.nickname }; + return new UserResponseDto(content.sub, content.nickname); } } diff --git a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts index ae8b126..74865a9 100644 --- a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Redirect, Req, UseGuards } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { AuthGuard } from '@nestjs/passport'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; @ApiTags('인증(auths)') @Controller('auths') @@ -37,6 +37,7 @@ export class AuthController { // 인증 테스트 api @Get('/tests') + @ApiBearerAuth() @UseGuards(AuthGuard('jwt')) test(@Req() req) { return req.user; diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 3b9addf..69f8659 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -53,7 +53,9 @@ export class CompetitionController { description: `주어진 대회 관련 정보를 이용해 대회를 생성한다. 과도한 DB 접근을 막기 위해, 하나의 대회에서 30개가 넘는 문제를 출제할 수 없도록 정책 상 제한한다.`, }) @ApiResponse({ type: CompetitionResponseDto }) + @ApiBearerAuth() @UsePipes(new ValidationPipe({ transform: true })) + @UseGuards(AuthGuard('jwt')) create(@Body() createCompetitionDto: CreateCompetitionDto) { return this.competitionService.create(createCompetitionDto); } diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index 19fe745..d689c9d 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -1,13 +1,17 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; +import { Competition } from '../entities/competition.entity'; + +import { User } from '@src/user/entities/user.entity'; + export class CreateCompetitionDto { constructor( name: string, detail: string, maxParticipants: number, - startsAt: string, - endsAt: string, + startsAt: Date, + endsAt: Date, problemIds: number[], ) { this.name = name; @@ -32,13 +36,25 @@ export class CreateCompetitionDto { @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) @IsNotEmpty() - startsAt: string; + startsAt: Date; @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) @IsNotEmpty() - endsAt: string; + endsAt: Date; @ApiProperty({ description: '대회에 사용되는 문제 id 리스트' }) @IsNotEmpty() problemIds: number[]; + + toEntity(user: User): Competition { + const competition = new Competition(); + competition.name = this.name; + competition.detail = this.detail; + competition.maxParticipants = this.maxParticipants; + competition.startsAt = this.startsAt; + competition.endsAt = this.endsAt; + competition.user = user; + competition.userId = user.id; + return competition; + } } diff --git a/be/algo-with-me-api/src/user/decorators/user.decorators.ts b/be/algo-with-me-api/src/user/decorators/user.decorators.ts new file mode 100644 index 0000000..95da447 --- /dev/null +++ b/be/algo-with-me-api/src/user/decorators/user.decorators.ts @@ -0,0 +1,9 @@ +import { ExecutionContext, createParamDecorator } from '@nestjs/common'; + +import { User } from '../entities/user.entity'; + +export const AuthUser = createParamDecorator((data: string, ctx: ExecutionContext) => { + const user: User = ctx.switchToHttp().getRequest().user; + if (!user) return; + return user; +}); diff --git a/be/algo-with-me-api/src/user/dto/user.response.dto.ts b/be/algo-with-me-api/src/user/dto/user.response.dto.ts index 5b3bd87..217c090 100644 --- a/be/algo-with-me-api/src/user/dto/user.response.dto.ts +++ b/be/algo-with-me-api/src/user/dto/user.response.dto.ts @@ -5,6 +5,7 @@ export class UserResponseDto { this.email = email; this.nickname = nickname; } + @ApiProperty({ description: '이메일' }) email: string; diff --git a/be/algo-with-me-api/src/user/entities/user.entity.ts b/be/algo-with-me-api/src/user/entities/user.entity.ts index 525e93c..b648740 100644 --- a/be/algo-with-me-api/src/user/entities/user.entity.ts +++ b/be/algo-with-me-api/src/user/entities/user.entity.ts @@ -8,11 +8,8 @@ import { UpdateDateColumn, } from 'typeorm'; -<<<<<<< HEAD import { Competition } from '@src/competition/entities/competition.entity'; import { CompetitionParticipant } from '@src/competition/entities/competition.participant.entity'; -======= ->>>>>>> 4e7f5da38dc17402cae27b808106db6a5adde1b4 import { Submission } from '@src/competition/entities/submission.entity'; @Entity() From cba05da000a6588fc43c59ac2b6ca72d54fb77fc Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:53:47 +0900 Subject: [PATCH 110/226] =?UTF-8?q?fix:=20=EC=A3=BC=EC=84=9D=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20migrationsRun=20=EC=98=B5=EC=85=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index a404f25..db87035 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -27,8 +27,7 @@ import { Competition } from '@src/competition/entities/competition.entity'; username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, - synchronize: true, // db 스키마(table) 자동 생성 - migrationsRun: true, // db 변경시 바로 적용 + synchronize: true, entities: [ Problem, Submission, From bc9cac9c28e93a21a33f0ef04e4273e8eb4ff4aa Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:25:10 +0900 Subject: [PATCH 111/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20=EC=A3=BC=EC=B5=9C=EC=9E=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20jwt=20=EC=9D=B8=EC=A6=9D=EC=8B=9C=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EC=A1=B4=EC=9E=AC=ED=95=98=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/auth/auth.strategy.ts | 11 ++++++++--- .../competition/controllers/competition.controller.ts | 6 ++++-- .../src/competition/dto/create-competition.dto.ts | 1 - .../src/competition/services/competition.service.ts | 8 +++++--- be/algo-with-me-api/src/user/services/user.service.ts | 5 +++++ 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/be/algo-with-me-api/src/auth/auth.strategy.ts b/be/algo-with-me-api/src/auth/auth.strategy.ts index 561eb77..2e0e50a 100644 --- a/be/algo-with-me-api/src/auth/auth.strategy.ts +++ b/be/algo-with-me-api/src/auth/auth.strategy.ts @@ -7,6 +7,7 @@ import { ExtractJwt, Strategy as PassportJwtStrategy } from 'passport-jwt'; import { AuthService } from './services/auth.service'; import { UserResponseDto } from '@src/user/dto/user.response.dto'; +import { User } from '@src/user/entities/user.entity'; import { UserService } from '@src/user/services/user.service'; @Injectable() @@ -34,7 +35,10 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') { @Injectable() export class JWTStrategy extends PassportStrategy(PassportJwtStrategy) { - constructor(private readonly configservice: ConfigService) { + constructor( + private readonly configservice: ConfigService, + private readonly userService: UserService, + ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, @@ -42,7 +46,8 @@ export class JWTStrategy extends PassportStrategy(PassportJwtStrategy) { }); } - async validate(content: any) { - return new UserResponseDto(content.sub, content.nickname); + async validate(content: any): Promise { + // user가 null이면 인가 실패 + return this.userService.getByEmail(content.sub); } } diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 69f8659..be9173a 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -21,6 +21,8 @@ import { UpdateCompetitionDto } from '../dto/update-competition.dto'; import { CompetitionService } from '../services/competition.service'; import { CompetitionResponseDto } from '@src/competition/dto/competition.response.dto'; +import { AuthUser } from '@src/user/decorators/user.decorators'; +import { User } from '@src/user/entities/user.entity'; @ApiTags('대회(competitions)') @Controller('competitions') @@ -56,8 +58,8 @@ export class CompetitionController { @ApiBearerAuth() @UsePipes(new ValidationPipe({ transform: true })) @UseGuards(AuthGuard('jwt')) - create(@Body() createCompetitionDto: CreateCompetitionDto) { - return this.competitionService.create(createCompetitionDto); + create(@Body() createCompetitionDto: CreateCompetitionDto, @AuthUser() user: User) { + return this.competitionService.create(createCompetitionDto, user); } @Put('/:competitionId') diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index d689c9d..db7c91e 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -53,7 +53,6 @@ export class CreateCompetitionDto { competition.maxParticipants = this.maxParticipants; competition.startsAt = this.startsAt; competition.endsAt = this.endsAt; - competition.user = user; competition.userId = user.id; return competition; } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index c915e36..ac9adc8 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -53,7 +53,7 @@ export class CompetitionService { return CompetitionResponseDto.from(competition); } - async create(createCompetitionDto: CreateCompetitionDto) { + async create(createCompetitionDto: CreateCompetitionDto, user: User) { this.assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto); const competitionProblems: CompetitionProblem[] = []; @@ -64,15 +64,17 @@ export class CompetitionService { competitionProblems.push(competitionProblem); } + const competition: Competition = createCompetitionDto.toEntity(user); + const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { - const competition: Competition = await this.competitionRepository.save(createCompetitionDto, { + const savedCompetition: Competition = await this.competitionRepository.save(competition, { transaction: false, }); for (const competitionProblem of competitionProblems) { - competitionProblem.competition = competition; + competitionProblem.competition = savedCompetition; await this.competitionProblemRepository.save(competitionProblem, { transaction: false }); } await queryRunner.commitTransaction(); diff --git a/be/algo-with-me-api/src/user/services/user.service.ts b/be/algo-with-me-api/src/user/services/user.service.ts index 80fa35f..551b78f 100644 --- a/be/algo-with-me-api/src/user/services/user.service.ts +++ b/be/algo-with-me-api/src/user/services/user.service.ts @@ -20,4 +20,9 @@ export class UserService { } return new UserResponseDto(user.email, user.nickname); } + + async getByEmail(email: string) { + const user = await this.userRepository.findOneBy({ email }); + return user; + } } From f81a2c5a60e65736c56798f508dec365d63f75c8 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:44:11 +0900 Subject: [PATCH 112/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=8B=9C=20=EC=9C=A0=EC=A0=80=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 5 ++++- .../src/competition/dto/update-competition.dto.ts | 8 ++++---- .../src/competition/services/competition.service.ts | 12 ++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index be9173a..eabcd64 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -68,12 +68,15 @@ export class CompetitionController { description: `URL의 파라미터(\`/:id\`)로 주어진 대회 id에 해당하는 대회 정보를 수정한다. request JSON 중 **수정하기를 원하는 것만** key: value 형식으로 요청한다.`, }) @ApiResponse({ type: Boolean }) + @ApiBearerAuth() + @UseGuards(AuthGuard('jwt')) @UsePipes(new ValidationPipe({ transform: true })) update( @Param('competitionId') competitionId: number, @Body() updateCompetitionDto: UpdateCompetitionDto, + @AuthUser() user: User, ) { - return this.competitionService.update(competitionId, updateCompetitionDto); + return this.competitionService.update(competitionId, updateCompetitionDto, user); } @Get('/:competitionId/problems') diff --git a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts index 952f78d..1484843 100644 --- a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts @@ -8,8 +8,8 @@ export class UpdateCompetitionDto { name: string, detail: string, maxParticipants: number, - startsAt: string, - endsAt: string, + startsAt: Date, + endsAt: Date, ) { this.name = name; this.detail = detail; @@ -32,11 +32,11 @@ export class UpdateCompetitionDto { @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) @Optional() - startsAt: string; + startsAt: Date; @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) @Optional() - endsAt: string; + endsAt: Date; toEntity(): Competition { const competition = new Competition(); diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index ac9adc8..c21ee98 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -1,5 +1,10 @@ import { InjectQueue } from '@nestjs/bull'; -import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Queue } from 'bull'; import { Server } from 'socket.io'; @@ -84,7 +89,10 @@ export class CompetitionService { } } - async update(id: number, updateCompetitionDto: UpdateCompetitionDto) { + async update(id: number, updateCompetitionDto: UpdateCompetitionDto, user: User) { + const competition: Competition = await this.competitionRepository.findOneBy({ id }); + if (!competition) throw new NotFoundException('대회를 찾을 수 없습니다.'); + if (competition.userId !== user.id) throw new UnauthorizedException('대회 주최자가 아닙니다.'); const result = await this.competitionRepository.update({ id: id }, { ...updateCompetitionDto }); return !!result.affected; } From e3b0a3f533a844cb08a58dd34e3a5b9373b9f50b Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:21:00 +0900 Subject: [PATCH 113/226] =?UTF-8?q?fix:=20=EC=9E=84=EC=8B=9C=20=EC=BB=A8?= =?UTF-8?q?=EC=8A=88=EB=A8=B8=20=EC=A3=BC=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/competition.module.ts | 3 +- .../src/competition/tem.consumer.ts | 40 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index b0507bb..a5729cc 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -11,7 +11,6 @@ import { Submission } from './entities/submission.entity'; import { CompetitionGateWay } from './gateways/competition.gateway'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; -import { SubmissionConsumer } from './tem.consumer'; import { Competition } from '@src/competition/entities/competition.entity'; import { User } from '@src/user/entities/user.entity'; @@ -31,6 +30,6 @@ import { User } from '@src/user/entities/user.entity'; }), ], controllers: [ProblemController, CompetitionController], - providers: [ProblemService, CompetitionService, SubmissionConsumer, CompetitionGateWay], + providers: [ProblemService, CompetitionService, CompetitionGateWay], }) export class CompetitionModule {} diff --git a/be/algo-with-me-api/src/competition/tem.consumer.ts b/be/algo-with-me-api/src/competition/tem.consumer.ts index 50ba961..5fe09d1 100644 --- a/be/algo-with-me-api/src/competition/tem.consumer.ts +++ b/be/algo-with-me-api/src/competition/tem.consumer.ts @@ -1,22 +1,22 @@ -import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; -import { Job } from 'bull'; +// import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +// import { Job } from 'bull'; -@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) -export class SubmissionConsumer { - @Process() - async transcode(job: Job ) { - console.log(job.data); - for (let i = 0; i < 10; i++) { - console.log(i); - } - return { good: 'good' }; - } +// @Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +// export class SubmissionConsumer { +// @Process() +// async transcode(job: Job ) { +// console.log(job.data); +// for (let i = 0; i < 10; i++) { +// console.log(i); +// } +// return { good: 'good' }; +// } - @OnQueueCompleted() - onCompleted(job: Job , result: any) { - console.log('job done'); - console.log(result); - // redis 에서 데이터 삭제 - job.remove(); - } -} +// @OnQueueCompleted() +// onCompleted(job: Job , result: any) { +// console.log('job done'); +// console.log(result); +// // redis 에서 데이터 삭제 +// job.remove(); +// } +// } From cddc27a82f1d60e4fce9b83309870878551f4b67 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 13:31:09 +0900 Subject: [PATCH 114/226] =?UTF-8?q?feat:=20websocket=20=EC=97=B0=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=ED=99=95=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/auth/auth.module.ts | 1 + .../src/auth/services/auth.service.ts | 16 ++++++++++++++-- .../src/competition/competition.module.ts | 2 ++ .../competition/gateways/competition.gateway.ts | 8 +++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/be/algo-with-me-api/src/auth/auth.module.ts b/be/algo-with-me-api/src/auth/auth.module.ts index 4bdc254..d9dc03e 100644 --- a/be/algo-with-me-api/src/auth/auth.module.ts +++ b/be/algo-with-me-api/src/auth/auth.module.ts @@ -23,5 +23,6 @@ import { UserModule } from '@src/user/user.module'; ], controllers: [AuthController], providers: [GithubStrategy, AuthService, JWTStrategy], + exports: [AuthService], }) export class AuthModule {} diff --git a/be/algo-with-me-api/src/auth/services/auth.service.ts b/be/algo-with-me-api/src/auth/services/auth.service.ts index c88b7d4..a270084 100644 --- a/be/algo-with-me-api/src/auth/services/auth.service.ts +++ b/be/algo-with-me-api/src/auth/services/auth.service.ts @@ -1,8 +1,9 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; @Injectable() export class AuthService { - constructor() {} + constructor(private readonly jwtService: JwtService) {} async getGithubPrimaryEmail(accessToken: string): Promise { const res: Response = await fetch('https://api.github.com/user/emails', { @@ -19,4 +20,15 @@ export class AuthService { }); return email['email']; } + + verifyToken(token: string) { + if (!token) throw new UnauthorizedException('토큰을 찾을 수 없습니다.'); + const tokens = token.split(' '); + if (tokens.length !== 2) throw new UnauthorizedException('토큰의 양식이 올바르지 않습니다.'); + try { + return this.jwtService.verify(tokens[1]); + } catch { + throw new UnauthorizedException('유효하지 않은 토큰입니다.'); + } + } } diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index a5729cc..f3e2038 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -12,6 +12,7 @@ import { CompetitionGateWay } from './gateways/competition.gateway'; import { CompetitionService } from './services/competition.service'; import { ProblemService } from './services/problem.service'; +import { AuthModule } from '@src/auth/auth.module'; import { Competition } from '@src/competition/entities/competition.entity'; import { User } from '@src/user/entities/user.entity'; @@ -28,6 +29,7 @@ import { User } from '@src/user/entities/user.entity'; BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), + AuthModule, ], controllers: [ProblemController, CompetitionController], providers: [ProblemService, CompetitionService, CompetitionGateWay], diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 0229cab..81ffbf1 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -14,12 +14,17 @@ import { Server, Socket } from 'socket.io'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { CompetitionService } from '../services/competition.service'; +import { AuthService } from '@src/auth/services/auth.service'; + @WebSocketGateway({ namespace: 'competitions' }) export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @WebSocketServer() server: Server; - constructor(private readonly competitionService: CompetitionService) {} + constructor( + private readonly competitionService: CompetitionService, + private readonly authService: AuthService, + ) {} afterInit(server: Server) { this.competitionService.server = server; @@ -54,6 +59,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { public handleConnection(client: Socket, ...args: any[]) { // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 const { competitionId } = client.handshake.query; + this.authService.verifyToken(client.handshake.headers.authorization); client.join(competitionId); console.log(client.id); console.log(competitionId, args); From 1ca2619d35649d61b33babcb7b73e973c759932e Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 13:59:59 +0900 Subject: [PATCH 115/226] =?UTF-8?q?feat:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9D=B8=EC=A6=9D=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateways/competition.gateway.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 81ffbf1..f97a77b 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,5 +1,6 @@ -import { UsePipes, ValidationPipe } from '@nestjs/common'; +import { UseFilters, UsePipes, ValidationPipe } from '@nestjs/common'; import { + BaseWsExceptionFilter, ConnectedSocket, MessageBody, OnGatewayConnection, @@ -57,11 +58,15 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { } public handleConnection(client: Socket, ...args: any[]) { - // TODO: 사용자가 대회 참여중인지 확인하는 로직 추가해야 함 - const { competitionId } = client.handshake.query; - this.authService.verifyToken(client.handshake.headers.authorization); - client.join(competitionId); - console.log(client.id); - console.log(competitionId, args); + try { + const { competitionId } = client.handshake.query; + this.authService.verifyToken(client.handshake.headers.authorization); + // TODO: 유저가 대회 참가했는지 검증 필요 + client.join(competitionId); + console.log(client.id); + console.log(competitionId, args); + } catch (error) { + client.emit('messages', { message: `${error.message}` }); + } } } From cea29911ace99ad2f58effcc8ea86277ef9057f5 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:06:08 +0900 Subject: [PATCH 116/226] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index f97a77b..5adac9e 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,6 +1,5 @@ -import { UseFilters, UsePipes, ValidationPipe } from '@nestjs/common'; +import { UsePipes, ValidationPipe } from '@nestjs/common'; import { - BaseWsExceptionFilter, ConnectedSocket, MessageBody, OnGatewayConnection, From accd026f9556789e837b2a93bfc2cac348cce99c Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:27:22 +0900 Subject: [PATCH 117/226] =?UTF-8?q?fix:=20websocket=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=A0=9C=EC=B6=9C=EC=8B=9C=20user,=20?= =?UTF-8?q?=EB=8C=80=ED=9A=8C=20=EA=B4=80=EB=A0=A8=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=97=86=EB=8D=98=EA=B2=83=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/dto/auth.token.payload.dto.ts | 9 ++++++ .../src/auth/services/auth.service.ts | 4 ++- .../src/competition/competition.module.ts | 2 ++ .../competition/dto/create-submission.dto.ts | 11 ++++++-- .../gateways/competition.gateway.ts | 28 ++++++++----------- .../services/competition.service.ts | 4 +-- 6 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 be/algo-with-me-api/src/auth/dto/auth.token.payload.dto.ts diff --git a/be/algo-with-me-api/src/auth/dto/auth.token.payload.dto.ts b/be/algo-with-me-api/src/auth/dto/auth.token.payload.dto.ts new file mode 100644 index 0000000..eca4cc3 --- /dev/null +++ b/be/algo-with-me-api/src/auth/dto/auth.token.payload.dto.ts @@ -0,0 +1,9 @@ +export class AuthTokenPayloadDto { + sub: string; + + nickname: string; + + iat: number; + + exp: number; +} diff --git a/be/algo-with-me-api/src/auth/services/auth.service.ts b/be/algo-with-me-api/src/auth/services/auth.service.ts index a270084..b0eb095 100644 --- a/be/algo-with-me-api/src/auth/services/auth.service.ts +++ b/be/algo-with-me-api/src/auth/services/auth.service.ts @@ -1,6 +1,8 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; +import { AuthTokenPayloadDto } from '../dto/auth.token.payload.dto'; + @Injectable() export class AuthService { constructor(private readonly jwtService: JwtService) {} @@ -21,7 +23,7 @@ export class AuthService { return email['email']; } - verifyToken(token: string) { + verifyToken(token: string): AuthTokenPayloadDto { if (!token) throw new UnauthorizedException('토큰을 찾을 수 없습니다.'); const tokens = token.split(' '); if (tokens.length !== 2) throw new UnauthorizedException('토큰의 양식이 올바르지 않습니다.'); diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index f3e2038..bb34737 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -15,6 +15,7 @@ import { ProblemService } from './services/problem.service'; import { AuthModule } from '@src/auth/auth.module'; import { Competition } from '@src/competition/entities/competition.entity'; import { User } from '@src/user/entities/user.entity'; +import { UserModule } from '@src/user/user.module'; @Module({ imports: [ @@ -30,6 +31,7 @@ import { User } from '@src/user/entities/user.entity'; name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), AuthModule, + UserModule, ], controllers: [ProblemController, CompetitionController], providers: [ProblemService, CompetitionService, CompetitionGateWay], diff --git a/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts b/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts index f19123a..77ebea3 100644 --- a/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-submission.dto.ts @@ -3,17 +3,24 @@ import { IsNotEmpty } from 'class-validator'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; +import { User } from '@src/user/entities/user.entity'; + export class CreateSubmissionDto { @IsNotEmpty() problemId: number; + @IsNotEmpty() + competitionId: number; + @IsNotEmpty() code: string; - toEntity(problem: Problem): Submission { + toEntity(problem: Problem, user: User): Submission { const submission = new Submission(); - submission.problem = problem; submission.code = this.code; + submission.competitionId = this.competitionId; + submission.problem = problem; + submission.user = user; return submission; } } diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 5adac9e..fefe599 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -14,7 +14,10 @@ import { Server, Socket } from 'socket.io'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; import { CompetitionService } from '../services/competition.service'; +import { AuthTokenPayloadDto } from '@src/auth/dto/auth.token.payload.dto'; import { AuthService } from '@src/auth/services/auth.service'; +import { User } from '@src/user/entities/user.entity'; +import { UserService } from '@src/user/services/user.service'; @WebSocketGateway({ namespace: 'competitions' }) export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @@ -24,32 +27,25 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { constructor( private readonly competitionService: CompetitionService, private readonly authService: AuthService, + private readonly userService: UserService, ) {} afterInit(server: Server) { this.competitionService.server = server; } - // @SubscribeMessage('events') - // handleEvent(@MessageBody() data: string, @ConnectedSocket() client: Socket): WsResponse { - // this.server.emit('events', { data: '데이터 간다' }); - // client.emit('events', { data: '데이터 간다22' }); - // this.server.to(client.id).emit('events', { data: '데이터 간다 33' }); - // const event = 'events'; - // console.log(client.id); - // console.log(client.rooms); - // console.log(data); - // return { event, data }; - // } - @SubscribeMessage('submissions') // TODO: 검증 실패시 에러 터져버리고, websocket으로 internal server error 가는거 수정해야됨. @UsePipes(new ValidationPipe({ transform: true })) - handleSubmission( + async handleSubmission( @MessageBody() createSubmissionDto: CreateSubmissionDto, @ConnectedSocket() client: Socket, - ): WsResponse { - this.competitionService.scoreSubmission(createSubmissionDto, client.id); + ): Promise > { + const result: AuthTokenPayloadDto = this.authService.verifyToken( + client.handshake.headers.authorization, + ); + const user: User = await this.userService.getByEmail(result.sub); + this.competitionService.scoreSubmission(createSubmissionDto, client.id, user); const event = 'messages'; const data = { message: '채점을 시작합니다.' }; console.log(createSubmissionDto); @@ -62,7 +58,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { this.authService.verifyToken(client.handshake.headers.authorization); // TODO: 유저가 대회 참가했는지 검증 필요 client.join(competitionId); - console.log(client.id); + console.log(client.id, client.rooms); console.log(competitionId, args); } catch (error) { client.emit('messages', { message: `${error.message}` }); diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index c21ee98..ddd0719 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -134,11 +134,11 @@ export class CompetitionService { this.competitionParticipantRepository.save({ competition: competition, user: user }); } - async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string) { + async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string, user:User) { const problem: Problem = await this.problemRepository.findOneBy({ id: createSubmissionDto.problemId, }); - const submission: Submission = createSubmissionDto.toEntity(problem); + const submission: Submission = createSubmissionDto.toEntity(problem, user); const savedSubmission: Submission = await this.submissionRepository.save(submission); await this.submissionQueue.add({ problemId: savedSubmission.problem.id, From 23c886ca0a0f9b8676acda80de23a1b2a6eeaa45 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:29:56 +0900 Subject: [PATCH 118/226] =?UTF-8?q?fix:=20=EC=9C=A0=EC=A0=80,=20=EB=8C=80?= =?UTF-8?q?=ED=9A=8C=20=EC=B6=94=EA=B0=80=EB=90=98=EB=A9=B4=EC=84=9C=20web?= =?UTF-8?q?socket=EC=9C=BC=EB=A1=9C=20=EC=A0=9C=EC=B6=9C=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index fefe599..365967e 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,5 +1,6 @@ -import { UsePipes, ValidationPipe } from '@nestjs/common'; +import { UseFilters, UsePipes, ValidationPipe } from '@nestjs/common'; import { + BaseWsExceptionFilter, ConnectedSocket, MessageBody, OnGatewayConnection, From 68fe75f9b7e05a5e4b82741b465ea3188998bfc5 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:06:08 +0900 Subject: [PATCH 119/226] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 365967e..fefe599 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -1,6 +1,5 @@ -import { UseFilters, UsePipes, ValidationPipe } from '@nestjs/common'; +import { UsePipes, ValidationPipe } from '@nestjs/common'; import { - BaseWsExceptionFilter, ConnectedSocket, MessageBody, OnGatewayConnection, From 23e674915de04d39bcfddc5153e4545c1400ff26 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 18:44:33 +0900 Subject: [PATCH 120/226] =?UTF-8?q?chore:=20=EB=8F=84=EC=BB=A4=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20API=20=EB=A7=9E=EC=B6=94=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/Dockerfile | 2 +- be/algo-with-me-docker/docker-sh/run.sh | 2 +- be/algo-with-me-docker/node-sh/run.sh | 11 ++++++----- be/algo-with-me-docker/node-sh/runJs.sh | 7 ++++--- be/algo-with-me-docker/src/app.ts | 8 ++++---- .../src/score/services/score.service.ts | 6 +++++- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index f8f8655..929600f 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -1,6 +1,6 @@ ARG NODE_VERSION=20.9.0 ARG ALPINE_VERSION=3.18 -ARG PORT=3000 +ARG PORT=5000 FROM node:${NODE_VERSION}-alpine AS base diff --git a/be/algo-with-me-docker/docker-sh/run.sh b/be/algo-with-me-docker/docker-sh/run.sh index 37bc9e0..2a38a0f 100755 --- a/be/algo-with-me-docker/docker-sh/run.sh +++ b/be/algo-with-me-docker/docker-sh/run.sh @@ -1,5 +1,5 @@ sudo docker run -d \ --p 3000:3000 \ +-p 5000:5000 \ -e COMPETITION_ID=$1 \ -e USER_ID=$2 \ -e PROBLEM_ID=$3 \ diff --git a/be/algo-with-me-docker/node-sh/run.sh b/be/algo-with-me-docker/node-sh/run.sh index 62948f0..efd8401 100755 --- a/be/algo-with-me-docker/node-sh/run.sh +++ b/be/algo-with-me-docker/node-sh/run.sh @@ -2,6 +2,7 @@ # $1 COMPETITION_ID # $2 USER_ID # $3 PROBLEM_ID +# $4 TESTCASE_ID # DESCRIPTION # SUBMISSION_JS_FILE에서 파일을 읽어, node로 실행한다. @@ -10,20 +11,20 @@ mkdir -p "/algo-with-me/submissions/$1/$2/" || exit 1 SUBMISSION_JS_FILE="/algo-with-me/submissions/$1/$2/$3.js" -DETAIL_FILE="/algo-with-me/submissions/$1/$2/$3.detail" +DETAIL_FILE="/algo-with-me/submissions/$1/$2/$3.$4.detail" # 제출된 js 파일이 있으면 node로 js 파일 실행 # 주의: judge.sh와 run.sh는 execute 권한이 부여되어야 함 if [ -f "$SUBMISSION_JS_FILE" ]; then - echo "[algo-with-me] run.sh: started running $SUBMISSION_JS_FILE" + echo "[algo-with-me] run.sh: started running $SUBMISSION_JS_FILE. COMPETITION_ID=$1, USER_ID=$2, PROBLEM_ID=$3, TESTCASE_ID=$4" # -o FILE Write result to FILE # -f FMT Custom format # U Total number of CPU-seconds that the process used directly (in user mode), in seconds. # e Elapsed real (wall clock) time used by the process, in seconds. # M Maximum resident set size of the process during its lifetime, in Kilobytes. - # /usr/bin/time -o "$DETAIL_FILE" -f "%e %M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" - /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" || exit 2 - echo "[algo-with-me] run.sh: successfully ran $SUBMISSION_JS_FILE" + # /usr/bin/time -o "$DETAIL_FILE" -f "%e %M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" "$4" + /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" "$4" || exit 2 + echo "[algo-with-me] run.sh: successfully ran $SUBMISSION_JS_FILE. COMPETITION_ID=$1, USER_ID=$2, PROBLEM_ID=$3, TESTCASE_ID=$4" else echo "[algo-with-me] run.sh: cannot find submitted js file $SUBMISSION_JS_FILE" exit 3 diff --git a/be/algo-with-me-docker/node-sh/runJs.sh b/be/algo-with-me-docker/node-sh/runJs.sh index 4afe4a8..b300165 100755 --- a/be/algo-with-me-docker/node-sh/runJs.sh +++ b/be/algo-with-me-docker/node-sh/runJs.sh @@ -2,6 +2,7 @@ # $1 COMPETITION_ID # $2 USER_ID # $3 PROBLEM_ID +# $4 TESTCASE_ID # DESCRIPTION # node로 제출한 파일을 실행한다. @@ -10,9 +11,9 @@ # node 실행할 때 첫번째 인자로 RESULT_FILE의 파일 경로를 입력해준다. SUBMISSION_JS_FILE="/algo-with-me/submissions/$1/$2/$3.js" -STDOUT_FILE="/algo-with-me/submissions/$1/$2/$3.stdout" -STDERR_FILE="/algo-with-me/submissions/$1/$2/$3.stderr" -RESULT_FILE="/algo-with-me/submissions/$1/$2/$3.result" +STDOUT_FILE="/algo-with-me/submissions/$1/$2/$3.$4.stdout" +STDERR_FILE="/algo-with-me/submissions/$1/$2/$3.$4.stderr" +RESULT_FILE="/algo-with-me/submissions/$1/$2/$3.$4.result" node "$SUBMISSION_JS_FILE" "$RESULT_FILE" 1> "$STDOUT_FILE" 2> "$STDERR_FILE" diff --git a/be/algo-with-me-docker/src/app.ts b/be/algo-with-me-docker/src/app.ts index 8909a18..028abae 100644 --- a/be/algo-with-me-docker/src/app.ts +++ b/be/algo-with-me-docker/src/app.ts @@ -3,7 +3,7 @@ import express from 'express'; import child_process from 'node:child_process'; const app = express(); -const PORT = 3000; +const PORT = 5000; const TIMEOUT_IN_MILLI = 10_000; function execute(cmd: string) { @@ -23,14 +23,14 @@ function getTimer(timeoutInMilli: number): Promise { }) } -app.post('/:competitionId/:userId/:problemId', (req, res) => { - const {competitionId, userId, problemId} = req.params; +app.post('/:competitionId/:userId/:problemId/:testcaseId', (req, res) => { + const {competitionId, userId, problemId, testcaseId} = req.params; // const result = execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId}`); // docker instance용 // const result = execute(`./node-sh/run.sh ${competitionId} ${userId} ${problemId}`); // local test용 const responseJson = { result: '', competitionId, userId, problemId }; Promise.race([ - execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId}`), + execute(`/algo-with-me/node-sh/run.sh ${competitionId} ${userId} ${problemId} ${testcaseId}`), getTimer(TIMEOUT_IN_MILLI), ]).then(() => { responseJson.result = 'SUCCESS'; diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 405cc78..a182ba2 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -77,7 +77,11 @@ export class ScoreService { problemId: number, testcaseId: number, ): Promise { - const url = `http://localhost:2000/${competitionId}/${userId}/${problemId}/${testcaseId}`; + const [dockerServerHost, dockerServerPort] = [ + process.env.DOCKER_SERVER_HOST, + process.env.DOCKER_SERVER_PORT, + ]; + const url = `http://${dockerServerHost}:${dockerServerPort}/${competitionId}/${userId}/${problemId}/${testcaseId}`; try { const response = await fetch(url, { method: 'POST' }); return (await response.json()) as ICoderunResponse; From 9ccf97365947c2177d68a315b4c84c8b70fd5a5c Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 21:19:50 +0900 Subject: [PATCH 121/226] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8F=AC=20=EC=A4=80?= =?UTF-8?q?=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/docker-sh/run.sh | 1 + be/algo-with-me-score/Dockerfile | 11 +++++++++++ be/algo-with-me-score/src/app.service.ts | 2 +- be/algo-with-me-score/src/main.ts | 2 +- .../src/score/services/filesystem.service.ts | 4 +--- .../src/score/services/score.consumer.ts | 5 ++--- .../src/score/services/score.service.ts | 8 ++++++-- 7 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 be/algo-with-me-score/Dockerfile diff --git a/be/algo-with-me-docker/docker-sh/run.sh b/be/algo-with-me-docker/docker-sh/run.sh index 2a38a0f..8c28171 100755 --- a/be/algo-with-me-docker/docker-sh/run.sh +++ b/be/algo-with-me-docker/docker-sh/run.sh @@ -3,6 +3,7 @@ sudo docker run -d \ -e COMPETITION_ID=$1 \ -e USER_ID=$2 \ -e PROBLEM_ID=$3 \ +-e TESTCASE_ID=$4 \ -v $HOME/algo-with-me/problems:/algo-with-me/problems:ro \ -v $HOME/algo-with-me/testcases:/algo-with-me/testcases:ro \ -v $HOME/algo-with-me/submissions:/algo-with-me/submissions \ diff --git a/be/algo-with-me-score/Dockerfile b/be/algo-with-me-score/Dockerfile new file mode 100644 index 0000000..d6ea7c5 --- /dev/null +++ b/be/algo-with-me-score/Dockerfile @@ -0,0 +1,11 @@ +FROM node:20 + +WORKDIR /algo-with-me-score +COPY ./ ./ + +RUN npm install -g pnpm \ + && pnpm install + +EXPOSE 4000 + +CMD ["pnpm", "run", "start:dev"] diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts index 191a952..e67dd89 100644 --- a/be/algo-with-me-score/src/app.service.ts +++ b/be/algo-with-me-score/src/app.service.ts @@ -6,7 +6,7 @@ import { MessageQueueItemDto } from './score/dtos/message-queue-item.dto'; @Injectable() export class AppService { - constructor(@InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private testQueue: Queue) {} + constructor(@InjectQueue('submission') private testQueue: Queue) {} async addMessageQueue(item: MessageQueueItemDto) { return await this.testQueue.add('score', item); diff --git a/be/algo-with-me-score/src/main.ts b/be/algo-with-me-score/src/main.ts index 50f62bb..77bfcfb 100644 --- a/be/algo-with-me-score/src/main.ts +++ b/be/algo-with-me-score/src/main.ts @@ -4,6 +4,6 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(3000); + await app.listen(4000); } bootstrap(); diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index aec97ab..2239846 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -30,8 +30,6 @@ export class FilesystemService { } private getMergedCode(code: string, frameCode: string) { - // TODO: 프레임코드 - const mergedCode = code + frameCode; - return mergedCode; + return frameCode.replace('${0}', code); } } diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 82bc782..a54e76a 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -4,15 +4,14 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Job } from 'bull'; import { Repository } from 'typeorm'; -import * as process from 'process'; - import { FilesystemService } from './filesystem.service'; import { ScoreService } from './score.service'; import { MessageQueueItemDto } from '../dtos/message-queue-item.dto'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; -@Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +// @Processor(process.env.REDIS_MESSAGE_QUEUE_NAME) +@Processor('submission') export class SubmissionConsumer { constructor( @InjectRepository(Submission) private readonly submissionRepository: Repository , diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index a182ba2..a983f3b 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -66,7 +66,9 @@ export class ScoreService { body: JSON.stringify(scoreResult), }); } catch (error) { - new Logger().error(`API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url})`); + new Logger().error( + `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, + ); throw new InternalServerErrorException(); } } @@ -86,7 +88,9 @@ export class ScoreService { const response = await fetch(url, { method: 'POST' }); return (await response.json()) as ICoderunResponse; } catch (error) { - new Logger().error(`도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url})`); + new Logger().error( + `도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, + ); throw new InternalServerErrorException(); } } From 39439a0ff1bfc5fe7730be0ab9fcacab41a9ef82 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 23 Nov 2023 21:32:33 +0900 Subject: [PATCH 122/226] =?UTF-8?q?chore:=20.dockerignore=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/.dockerignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 be/algo-with-me-score/.dockerignore diff --git a/be/algo-with-me-score/.dockerignore b/be/algo-with-me-score/.dockerignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/be/algo-with-me-score/.dockerignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ From 7191278679144d8e2884ffad1f963c0df6c5a3f8 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 24 Nov 2023 00:28:41 +0900 Subject: [PATCH 123/226] =?UTF-8?q?chore:=20test=20=EC=99=84=EB=A3=8C.=20?= =?UTF-8?q?=ED=94=84=EB=A0=88=EC=9E=84=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EC=99=84=EB=A3=8C.=20(=EB=B0=B1=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=20=EC=B0=B8=EA=B3=A0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/docker-sh/run.sh | 4 ++-- be/algo-with-me-docker/node-sh/runJs.sh | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/be/algo-with-me-docker/docker-sh/run.sh b/be/algo-with-me-docker/docker-sh/run.sh index 8c28171..443084d 100755 --- a/be/algo-with-me-docker/docker-sh/run.sh +++ b/be/algo-with-me-docker/docker-sh/run.sh @@ -4,8 +4,8 @@ sudo docker run -d \ -e USER_ID=$2 \ -e PROBLEM_ID=$3 \ -e TESTCASE_ID=$4 \ --v $HOME/algo-with-me/problems:/algo-with-me/problems:ro \ --v $HOME/algo-with-me/testcases:/algo-with-me/testcases:ro \ +-v $HOME/algo-with-me/problems:/algo-with-me/problems \ +-v $HOME/algo-with-me/testcases:/algo-with-me/testcases \ -v $HOME/algo-with-me/submissions:/algo-with-me/submissions \ --user "$(id -u)":"$(id -g)" \ --name algo-with-me-docker \ diff --git a/be/algo-with-me-docker/node-sh/runJs.sh b/be/algo-with-me-docker/node-sh/runJs.sh index b300165..29199d1 100755 --- a/be/algo-with-me-docker/node-sh/runJs.sh +++ b/be/algo-with-me-docker/node-sh/runJs.sh @@ -14,7 +14,10 @@ SUBMISSION_JS_FILE="/algo-with-me/submissions/$1/$2/$3.js" STDOUT_FILE="/algo-with-me/submissions/$1/$2/$3.$4.stdout" STDERR_FILE="/algo-with-me/submissions/$1/$2/$3.$4.stderr" RESULT_FILE="/algo-with-me/submissions/$1/$2/$3.$4.result" +TIME_FILE="/algo-with-me/submissions/$1/$2/$3.$4.time" +MEMORY_FILE="/algo-with-me/submissions/$1/$2/$3.$4.memory" +TESTCASE_INPUTFILE="/algo-with-me/testcases/$3/secrets/$4.in" -node "$SUBMISSION_JS_FILE" "$RESULT_FILE" 1> "$STDOUT_FILE" 2> "$STDERR_FILE" +node "$SUBMISSION_JS_FILE" "$TESTCASE_INPUTFILE" "$RESULT_FILE" "$TIME_FILE" "$MEMORY_FILE" 1> "$STDOUT_FILE" 2> "$STDERR_FILE" exit 0 From 4c0e99129ff0f834927a53b9680877342ea52f5d Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 24 Nov 2023 00:40:57 +0900 Subject: [PATCH 124/226] =?UTF-8?q?chore:=20entity=20api=20=EB=B3=B5?= =?UTF-8?q?=EB=B6=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/package.json | 1 + be/algo-with-me-score/pnpm-lock.yaml | 39 ++++++++++++++---- be/algo-with-me-score/src/app.module.ts | 11 ++++- .../src/score/dtos/score-result.dto.ts | 2 +- .../src/score/entities/competition.entity.ts | 15 +++++++ ...bmission.enums.ts => competition.enums.ts} | 0 .../competition.participant.entity.ts | 35 ++++++++++++++++ .../entities/competition.problem.entity.ts | 14 +++++-- .../src/score/entities/submission.entity.ts | 14 ++++--- .../src/score/entities/user.entity.ts | 41 +++++++++++++++++++ .../src/score/score.module.ts | 11 ++++- .../src/score/services/score.service.ts | 2 +- 12 files changed, 164 insertions(+), 21 deletions(-) rename be/algo-with-me-score/src/score/entities/{submission.enums.ts => competition.enums.ts} (100%) create mode 100644 be/algo-with-me-score/src/score/entities/competition.participant.entity.ts create mode 100644 be/algo-with-me-score/src/score/entities/user.entity.ts diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index 3d4b070..046baaa 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -27,6 +27,7 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.1", "bull": "^4.11.5", + "class-validator": "^0.14.0", "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index e78375c..e52dd53 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -10,7 +10,7 @@ dependencies: version: 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(bull@4.11.5) '@nestjs/common': specifier: ^10.0.0 - version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + version: 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/config': specifier: ^3.1.1 version: 3.1.1(@nestjs/common@10.2.8)(reflect-metadata@0.1.13) @@ -26,6 +26,9 @@ dependencies: bull: specifier: ^4.11.5 version: 4.11.5 + class-validator: + specifier: ^0.14.0 + version: 0.14.0 pg: specifier: ^8.11.3 version: 8.11.3 @@ -925,7 +928,7 @@ packages: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 dependencies: - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) tslib: 2.6.0 dev: false @@ -938,7 +941,7 @@ packages: bull: ^3.3 || ^4.0.0 dependencies: '@nestjs/bull-shared': 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) bull: 4.11.5 tslib: 2.6.0 @@ -986,7 +989,7 @@ packages: - webpack-cli dev: true - /@nestjs/common@10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1): + /@nestjs/common@10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1): resolution: {integrity: sha512-rmpwcdvq2IWMmsUVP8rsdKub6uDWk7dwCYo0aif50JTwcvcxzaP3iKVFKoSgvp0RKYu8h15+/AEOfaInmPpl0Q==} peerDependencies: class-transformer: '*' @@ -999,6 +1002,7 @@ packages: class-validator: optional: true dependencies: + class-validator: 0.14.0 iterare: 1.2.1 reflect-metadata: 0.1.13 rxjs: 7.8.1 @@ -1011,7 +1015,7 @@ packages: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 reflect-metadata: ^0.1.13 dependencies: - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) dotenv: 16.3.1 dotenv-expand: 10.0.0 lodash: 4.17.21 @@ -1037,7 +1041,7 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 @@ -1056,7 +1060,7 @@ packages: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 dependencies: - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) body-parser: 1.20.2 cors: 2.8.5 @@ -1094,7 +1098,7 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) tslib: 2.6.2 @@ -1109,7 +1113,7 @@ packages: rxjs: ^7.2.0 typeorm: ^0.3.0 dependencies: - '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.8(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) reflect-metadata: 0.1.13 rxjs: 7.8.1 @@ -1380,6 +1384,9 @@ packages: '@types/superagent': 4.1.21 dev: true + /@types/validator@13.11.7: + resolution: {integrity: sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==} + /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} dev: true @@ -2190,6 +2197,13 @@ packages: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} dev: true + /class-validator@0.14.0: + resolution: {integrity: sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==} + dependencies: + '@types/validator': 13.11.7 + libphonenumber-js: 1.10.51 + validator: 13.11.0 + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -4450,6 +4464,9 @@ packages: type-check: 0.4.0 dev: true + /libphonenumber-js@1.10.51: + resolution: {integrity: sha512-vY2I+rQwrDQzoPds0JeTEpeWzbUJgqoV0O4v31PauHBb/e+1KCXKylHcDnBMgJZ9fH9mErsEbROJY3Z3JtqEmg==} + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -6267,6 +6284,10 @@ packages: convert-source-map: 2.0.0 dev: true + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 233b4c6..1ee3997 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -8,9 +8,11 @@ import * as process from 'node:process'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { Competition } from './score/entities/competition.entity'; +import { CompetitionParticipant } from './score/entities/competition.participant.entity'; import { CompetitionProblem } from './score/entities/competition.problem.entity'; import { Problem } from './score/entities/problem.entity'; import { Submission } from './score/entities/submission.entity'; +import { User } from './score/entities/user.entity'; import { ScoreModule } from './score/score.module'; @Module({ @@ -27,7 +29,14 @@ import { ScoreModule } from './score/score.module'; password: process.env.DB_PASSWORD, database: process.env.DB_NAME, synchronize: false, - entities: [Competition, CompetitionProblem, Submission, Problem], + entities: [ + Competition, + Submission, + Problem, + User, + CompetitionProblem, + CompetitionParticipant, + ], }), BullModule.forRoot({ redis: { diff --git a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts index 3733d4e..9af8383 100644 --- a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts +++ b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts @@ -1,4 +1,4 @@ -import { RESULT } from '../entities/submission.enums'; +import { RESULT } from '../entities/competition.enums'; export class ScoreResultDto { constructor( diff --git a/be/algo-with-me-score/src/score/entities/competition.entity.ts b/be/algo-with-me-score/src/score/entities/competition.entity.ts index 370fc82..19fc07c 100644 --- a/be/algo-with-me-score/src/score/entities/competition.entity.ts +++ b/be/algo-with-me-score/src/score/entities/competition.entity.ts @@ -2,13 +2,16 @@ import { Column, CreateDateColumn, Entity, + ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { CompetitionParticipant } from './competition.participant.entity'; import { CompetitionProblem } from './competition.problem.entity'; import { Submission } from './submission.entity'; +import { User } from './user.entity'; @Entity() export class Competition { @@ -36,6 +39,18 @@ export class Competition { @OneToMany(() => CompetitionProblem, (competitionProblem) => competitionProblem.competition) competitionProblems: CompetitionProblem[]; + @OneToMany( + () => CompetitionParticipant, + (competitionParticipant) => competitionParticipant.competition, + ) + competitionParticipants: CompetitionParticipant[]; + + @Column() + userId: number; + + @ManyToOne(() => User, (user) => user.competitions, { nullable: false }) + user: User; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-score/src/score/entities/submission.enums.ts b/be/algo-with-me-score/src/score/entities/competition.enums.ts similarity index 100% rename from be/algo-with-me-score/src/score/entities/submission.enums.ts rename to be/algo-with-me-score/src/score/entities/competition.enums.ts diff --git a/be/algo-with-me-score/src/score/entities/competition.participant.entity.ts b/be/algo-with-me-score/src/score/entities/competition.participant.entity.ts new file mode 100644 index 0000000..158d7ec --- /dev/null +++ b/be/algo-with-me-score/src/score/entities/competition.participant.entity.ts @@ -0,0 +1,35 @@ +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + Unique, +} from 'typeorm'; + +import { Competition } from './competition.entity'; +import { User } from './user.entity'; + +@Entity() +@Unique('unique_participant', ['user', 'competition']) +export class CompetitionParticipant { + @PrimaryGeneratedColumn() + id: number; + + @Column() + userId: number; + + @ManyToOne(() => User, (user) => user.competitionParticipant, { nullable: false }) + user: User; + + @Column() + competitionId: number; + + @ManyToOne(() => Competition, (competition) => competition.competitionParticipants, { + nullable: false, + }) + competition: Competition; + + @CreateDateColumn() + createdAt: Date; +} diff --git a/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts b/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts index 02dde8c..1c1399f 100644 --- a/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts +++ b/be/algo-with-me-score/src/score/entities/competition.problem.entity.ts @@ -1,4 +1,4 @@ -import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { Competition } from './competition.entity'; import { Problem } from './problem.entity'; @@ -8,10 +8,18 @@ export class CompetitionProblem { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => Competition, (competition) => competition.competitionProblems) + @Column() + competitionId: number; + + @ManyToOne(() => Competition, (competition) => competition.competitionProblems, { + nullable: false, + }) competition: Competition; - @ManyToOne(() => Problem, (problem) => problem.competitionProblems) + @Column() + problemId: number; + + @ManyToOne(() => Problem, (problem) => problem.competitionProblems, { nullable: false }) problem: Problem; @CreateDateColumn() diff --git a/be/algo-with-me-score/src/score/entities/submission.entity.ts b/be/algo-with-me-score/src/score/entities/submission.entity.ts index 0e3682f..bf134ac 100644 --- a/be/algo-with-me-score/src/score/entities/submission.entity.ts +++ b/be/algo-with-me-score/src/score/entities/submission.entity.ts @@ -2,15 +2,15 @@ import { Column, CreateDateColumn, Entity, - JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { Competition } from './competition.entity'; +import { RESULT } from './competition.enums'; import { Problem } from './problem.entity'; -import { RESULT } from './submission.enums'; +import { User } from './user.entity'; @Entity() export class Submission { @@ -34,16 +34,20 @@ export class Submission { problemId: number; @ManyToOne(() => Problem, (problem) => problem.submissions, { nullable: false }) - @JoinColumn({ name: 'problemId', referencedColumnName: 'id' }) problem: Problem; @Column() competitionId: number; - @ManyToOne(() => Competition, (competition) => competition.submissions) - @JoinColumn({ name: 'competitionId', referencedColumnName: 'id' }) + @ManyToOne(() => Competition, (competition) => competition.submissions, { nullable: false }) competition: Competition; + @Column() + userId: number; + + @ManyToOne(() => User, (user) => user.submissions, { nullable: false }) + user: User; + @CreateDateColumn() createdAt: Date; diff --git a/be/algo-with-me-score/src/score/entities/user.entity.ts b/be/algo-with-me-score/src/score/entities/user.entity.ts new file mode 100644 index 0000000..a505eff --- /dev/null +++ b/be/algo-with-me-score/src/score/entities/user.entity.ts @@ -0,0 +1,41 @@ +import { IsEmail } from 'class-validator'; +import { + Column, + CreateDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { Competition } from './competition.entity'; +import { CompetitionParticipant } from './competition.participant.entity'; +import { Submission } from './submission.entity'; + +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number; + + @Column() + @IsEmail() + email: string; + + @Column() + nickname: string; + + @OneToMany(() => CompetitionParticipant, (competitionParticipant) => competitionParticipant.user) + competitionParticipant: CompetitionParticipant[]; + + @OneToMany(() => Submission, (submission) => submission.user) + submissions: Submission[]; + + @OneToMany(() => Competition, (competition) => competition.user) + competitions: Competition[]; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/be/algo-with-me-score/src/score/score.module.ts b/be/algo-with-me-score/src/score/score.module.ts index 1785dd9..aafa4a3 100644 --- a/be/algo-with-me-score/src/score/score.module.ts +++ b/be/algo-with-me-score/src/score/score.module.ts @@ -3,16 +3,25 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Competition } from './entities/competition.entity'; +import { CompetitionParticipant } from './entities/competition.participant.entity'; import { CompetitionProblem } from './entities/competition.problem.entity'; import { Problem } from './entities/problem.entity'; import { Submission } from './entities/submission.entity'; +import { User } from './entities/user.entity'; import { FilesystemService } from './services/filesystem.service'; import { SubmissionConsumer } from './services/score.consumer'; import { ScoreService } from './services/score.service'; @Module({ imports: [ - TypeOrmModule.forFeature([Competition, CompetitionProblem, Submission, Problem]), + TypeOrmModule.forFeature([ + Competition, + Submission, + Problem, + User, + CompetitionProblem, + CompetitionParticipant, + ]), BullModule.registerQueue({ name: process.env.REDIS_MESSAGE_QUEUE_NAME, }), diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index a983f3b..606cd8e 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -3,8 +3,8 @@ import { InternalServerErrorException, Logger } from '@nestjs/common'; import * as fs from 'node:fs'; import { ScoreResultDto } from '../dtos/score-result.dto'; +import { RESULT } from '../entities/competition.enums'; import { Submission } from '../entities/submission.entity'; -import { RESULT } from '../entities/submission.enums'; import ICoderunResponse from '../interfaces/coderun-response.interface'; export class ScoreService { From 1371bec8a50a4a795a1518cbafe81ebf78e7a25c Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:43:34 +0900 Subject: [PATCH 125/226] =?UTF-8?q?fix:=20=ED=86=A0=ED=81=B0=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=A4=ED=8C=A8=EC=8B=9C=20=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EC=A2=85=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index f24b4b1..f45d688 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -62,6 +62,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { console.log(competitionId, args); } catch (error) { client.emit('messages', { message: `${error.message}` }); + client.disconnect(); } } } From 80f68c41b9d8a56742ce1036a4736e78dddc8e09 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:05:10 +0900 Subject: [PATCH 126/226] =?UTF-8?q?fix:=20websocket=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=EC=8B=9C=20=EC=9C=A0=EC=A0=80=EA=B0=80=20=EB=8C=80=ED=9A=8C=20?= =?UTF-8?q?=EC=B0=B8=EC=97=AC=EC=9E=90=EC=9D=B8=EC=A7=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 13 ++++++++----- .../src/competition/services/competition.service.ts | 11 ++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index f45d688..635f822 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -41,10 +41,10 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @MessageBody() createSubmissionDto: CreateSubmissionDto, @ConnectedSocket() client: Socket, ): Promise > { - const result: AuthTokenPayloadDto = this.authService.verifyToken( + const authTokenPayloadDto: AuthTokenPayloadDto = this.authService.verifyToken( client.handshake.headers.authorization, ); - const user: User = await this.userService.getByEmail(result.sub); + const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); this.competitionService.scoreSubmission(createSubmissionDto, client.id, user); const event = 'messages'; const data = { message: '채점을 시작합니다.' }; @@ -52,11 +52,14 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { return { event, data }; } - public handleConnection(client: Socket, ...args: any[]) { + public async handleConnection(client: Socket, ...args: any[]) { try { const { competitionId } = client.handshake.query; - this.authService.verifyToken(client.handshake.headers.authorization); - // TODO: 유저가 대회 참가했는지 검증 필요 + const authTokenPayloadDto: AuthTokenPayloadDto = this.authService.verifyToken( + client.handshake.headers.authorization, + ); + const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); + await this.competitionService.isUserJoinedCompetition(Number(competitionId), user.id); client.join(competitionId); console.log(client.id); console.log(competitionId, args); diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index ddd0719..b66eea5 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -134,7 +134,16 @@ export class CompetitionService { this.competitionParticipantRepository.save({ competition: competition, user: user }); } - async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string, user:User) { + async isUserJoinedCompetition(competitionId: number, userId: number) { + console.log(competitionId, userId); + const competitionParticipant: CompetitionParticipant = + await this.competitionParticipantRepository.findOneBy({ competitionId, userId }); + + if (!competitionParticipant) throw new UnauthorizedException('대회 참여자가 아닙니다.'); + return true; + } + + async scoreSubmission(createSubmissionDto: CreateSubmissionDto, socketId: string, user: User) { const problem: Problem = await this.problemRepository.findOneBy({ id: createSubmissionDto.problemId, }); From 1a45559548166ac2708e96df8360de50b0058be0 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:15:42 +0900 Subject: [PATCH 127/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=EC=97=90=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=EC=B6=94=EA=B0=80,=20websocket=20?= =?UTF-8?q?=EC=B1=84=EC=A0=90=20=EC=8B=9C=EC=9E=91=EC=8B=9C=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EB=8A=94=20=EA=B0=92=EC=97=90=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EA=B0=9C=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/dto/problem.response.dto.ts | 6 ++++++ .../src/competition/gateways/competition.gateway.ts | 9 ++++++++- .../src/competition/services/problem.service.ts | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts b/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts index 4757fa1..08654e8 100644 --- a/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/problem.response.dto.ts @@ -9,6 +9,7 @@ export class ProblemResponseDto { timeLimit: number, memoryLimit: number, content: string, + testcaseNum: number, createdAt: Date, ) { this.id = id; @@ -16,6 +17,7 @@ export class ProblemResponseDto { this.timeLimit = timeLimit; this.memoryLimit = memoryLimit; this.content = content; + this.testcaseNum = testcaseNum; this.createdAt = createdAt; } @@ -34,6 +36,9 @@ export class ProblemResponseDto { @ApiProperty({ description: '문제 내용' }) content: string; + @ApiProperty({ description: '테스트케이스 개수' }) + testcaseNum: number; + @ApiProperty() createdAt: Date; @@ -44,6 +49,7 @@ export class ProblemResponseDto { problem.timeLimit, problem.memoryLimit, content, + problem.testcaseNum, problem.createdAt, ); } diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 635f822..824931b 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -12,7 +12,10 @@ import { import { Server, Socket } from 'socket.io'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; +import { ProblemResponseDto } from '../dto/problem.response.dto'; +import { Problem } from '../entities/problem.entity'; import { CompetitionService } from '../services/competition.service'; +import { ProblemService } from '../services/problem.service'; import { AuthTokenPayloadDto } from '@src/auth/dto/auth.token.payload.dto'; import { AuthService } from '@src/auth/services/auth.service'; @@ -28,6 +31,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { private readonly competitionService: CompetitionService, private readonly authService: AuthService, private readonly userService: UserService, + private readonly problemService: ProblemService, ) {} afterInit(server: Server) { @@ -45,9 +49,12 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { client.handshake.headers.authorization, ); const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); + const problem: ProblemResponseDto = await this.problemService.findOne( + createSubmissionDto.problemId, + ); this.competitionService.scoreSubmission(createSubmissionDto, client.id, user); const event = 'messages'; - const data = { message: '채점을 시작합니다.' }; + const data = { message: '채점을 시작합니다.', testcaseNum: problem.testcaseNum }; console.log(createSubmissionDto); return { event, data }; } diff --git a/be/algo-with-me-api/src/competition/services/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts index a7b3c31..6f0b7d3 100644 --- a/be/algo-with-me-api/src/competition/services/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -28,7 +28,7 @@ export class ProblemService { }); } - async findOne(id: number) { + async findOne(id: number): Promise { const problem = await this.problemRepository.findOneBy({ id }); const fileName = id.toString() + '.md'; const paths = path.join(process.env.PROBLEM_PATH, fileName); From c26db292782cc94535e2d14ba3276fa9bfd793b3 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:23:28 +0900 Subject: [PATCH 128/226] =?UTF-8?q?fix:=20=EC=B1=84=EC=A0=90=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20api=EC=97=90=EC=84=9C=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/competition/dto/score-result.dto.ts | 4 ---- .../src/competition/gateways/competition.gateway.ts | 4 ++-- .../src/competition/services/competition.service.ts | 2 +- .../src/competition/services/problem.service.ts | 5 +++++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts index a7222f8..0c1e3b9 100644 --- a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts @@ -8,10 +8,6 @@ export class ScoreResultDto { @IsNotEmpty() submissionId: number; - @ApiProperty() - @IsNotEmpty() - problemId: number; - @ApiProperty() @IsNotEmpty() testcaseId: number; diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index 824931b..db09372 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -49,12 +49,12 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { client.handshake.headers.authorization, ); const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); - const problem: ProblemResponseDto = await this.problemService.findOne( + const testcaseNum: number = await this.problemService.getProblenTestcaseNum( createSubmissionDto.problemId, ); this.competitionService.scoreSubmission(createSubmissionDto, client.id, user); const event = 'messages'; - const data = { message: '채점을 시작합니다.', testcaseNum: problem.testcaseNum }; + const data = { message: '채점을 시작합니다.', testcaseNum: testcaseNum }; console.log(createSubmissionDto); return { event, data }; } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index b66eea5..ea528b8 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -174,7 +174,7 @@ export class CompetitionService { submission.detail.push(result); this.submissionRepository.save(submission); - result['problemId'] = scoreResultDto.problemId; + result['problemId'] = submission.problemId; result['stdOut'] = scoreResultDto.stdOut; this.server.to(scoreResultDto.socketId).emit('scoreResult', result); } diff --git a/be/algo-with-me-api/src/competition/services/problem.service.ts b/be/algo-with-me-api/src/competition/services/problem.service.ts index 6f0b7d3..e1561d5 100644 --- a/be/algo-with-me-api/src/competition/services/problem.service.ts +++ b/be/algo-with-me-api/src/competition/services/problem.service.ts @@ -37,6 +37,11 @@ export class ProblemService { return ProblemResponseDto.from(problem, content); } + async getProblenTestcaseNum(id: number) { + const problem = await this.problemRepository.findOneBy({ id }); + return problem.testcaseNum; + } + // update(id: number, updateCompetitionDto: UpdateCompetitionDto) { // return `This action updates a #${id} competition`; // } From 160895ee3d516d3f85d8340d312ba89d09ec76f4 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:24:09 +0900 Subject: [PATCH 129/226] =?UTF-8?q?fix:=20=EC=95=88=EC=93=B0=EB=8A=94=20im?= =?UTF-8?q?port=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index db09372..d6530d1 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -12,8 +12,6 @@ import { import { Server, Socket } from 'socket.io'; import { CreateSubmissionDto } from '../dto/create-submission.dto'; -import { ProblemResponseDto } from '../dto/problem.response.dto'; -import { Problem } from '../entities/problem.entity'; import { CompetitionService } from '../services/competition.service'; import { ProblemService } from '../services/problem.service'; From 47cb6871f22466f870f4cb6d70e4f5d3bbde9188 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 03:16:59 +0900 Subject: [PATCH 130/226] =?UTF-8?q?fix:=20bull=20queue=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=ED=95=98=EB=93=9C=EC=BD=94=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/competition/competition.module.ts | 2 +- .../src/competition/services/competition.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/competition.module.ts b/be/algo-with-me-api/src/competition/competition.module.ts index bb34737..7bfbe05 100644 --- a/be/algo-with-me-api/src/competition/competition.module.ts +++ b/be/algo-with-me-api/src/competition/competition.module.ts @@ -28,7 +28,7 @@ import { UserModule } from '@src/user/user.module'; User, ]), BullModule.registerQueue({ - name: process.env.REDIS_MESSAGE_QUEUE_NAME, + name: 'submission', }), AuthModule, UserModule, diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index ea528b8..7ebc616 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -41,7 +41,7 @@ export class CompetitionService { @InjectRepository(User) private readonly userRepository: Repository , @InjectRepository(CompetitionParticipant) private readonly competitionParticipantRepository: Repository , - @InjectQueue(process.env.REDIS_MESSAGE_QUEUE_NAME) private submissionQueue: Queue, + @InjectQueue('submission') private submissionQueue: Queue, private dataSource: DataSource, ) {} From ac62da1cef7f19caf8e7323efdbf5098dc2da6f9 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:28:16 +0900 Subject: [PATCH 131/226] =?UTF-8?q?fix:=20dotenv=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EC=88=98=EC=A0=95,=20=EC=9B=B9=EC=86=8C?= =?UTF-8?q?=EC=BC=93=20=EA=B2=80=EC=A6=9D=20=EC=A3=BC=EC=84=9D=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/app.module.ts | 3 +++ .../src/competition/gateways/competition.gateway.ts | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/be/algo-with-me-api/src/app.module.ts b/be/algo-with-me-api/src/app.module.ts index db87035..6c618c0 100644 --- a/be/algo-with-me-api/src/app.module.ts +++ b/be/algo-with-me-api/src/app.module.ts @@ -2,6 +2,7 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { config } from 'dotenv'; import { AuthModule } from './auth/auth.module'; import { CompetitionModule } from './competition/competition.module'; @@ -14,6 +15,8 @@ import { UserModule } from './user/user.module'; import { Competition } from '@src/competition/entities/competition.entity'; +config(); + @Module({ imports: [ ConfigModule.forRoot({ diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index d6530d1..fde2f2d 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -60,11 +60,12 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { public async handleConnection(client: Socket, ...args: any[]) { try { const { competitionId } = client.handshake.query; - const authTokenPayloadDto: AuthTokenPayloadDto = this.authService.verifyToken( - client.handshake.headers.authorization, - ); - const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); - await this.competitionService.isUserJoinedCompetition(Number(competitionId), user.id); + // 검증 로직 주석처리 + // const authTokenPayloadDto: AuthTokenPayloadDto = this.authService.verifyToken( + // client.handshake.headers.authorization, + // ); + // const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); + // await this.competitionService.isUserJoinedCompetition(Number(competitionId), user.id); client.join(competitionId); console.log(client.id); console.log(competitionId, args); From b238ae7c461239c3663b789c96840fa9a7487567 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:47:26 +0900 Subject: [PATCH 132/226] =?UTF-8?q?fix:=20dotenv=20=EC=84=A4=EC=B9=98,=20s?= =?UTF-8?q?core=20=EC=84=9C=EB=B2=84=20dotenv=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/package.json | 1 + be/algo-with-me-api/pnpm-lock.yaml | 4 +++- be/algo-with-me-score/package.json | 1 + be/algo-with-me-score/pnpm-lock.yaml | 4 +++- be/algo-with-me-score/src/app.module.ts | 5 ++++- be/algo-with-me-score/src/score/score.module.ts | 4 ---- be/algo-with-me-score/src/score/services/score.consumer.ts | 3 ++- 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/be/algo-with-me-api/package.json b/be/algo-with-me-api/package.json index b5dfdf1..9586045 100644 --- a/be/algo-with-me-api/package.json +++ b/be/algo-with-me-api/package.json @@ -53,6 +53,7 @@ "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "dotenv": "^16.3.1", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.1", diff --git a/be/algo-with-me-api/pnpm-lock.yaml b/be/algo-with-me-api/pnpm-lock.yaml index 7c6f66f..477714f 100644 --- a/be/algo-with-me-api/pnpm-lock.yaml +++ b/be/algo-with-me-api/pnpm-lock.yaml @@ -100,6 +100,9 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.0.0 version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + dotenv: + specifier: ^16.3.1 + version: 16.3.1 eslint: specifier: ^8.42.0 version: 8.53.0 @@ -2781,7 +2784,6 @@ packages: /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} - dev: false /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} diff --git a/be/algo-with-me-score/package.json b/be/algo-with-me-score/package.json index 046baaa..c97b0cf 100644 --- a/be/algo-with-me-score/package.json +++ b/be/algo-with-me-score/package.json @@ -43,6 +43,7 @@ "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "dotenv": "^16.3.1", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.1", diff --git a/be/algo-with-me-score/pnpm-lock.yaml b/be/algo-with-me-score/pnpm-lock.yaml index e52dd53..cace872 100644 --- a/be/algo-with-me-score/pnpm-lock.yaml +++ b/be/algo-with-me-score/pnpm-lock.yaml @@ -70,6 +70,9 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.0.0 version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + dotenv: + specifier: ^16.3.1 + version: 16.3.1 eslint: specifier: ^8.42.0 version: 8.53.0 @@ -2604,7 +2607,6 @@ packages: /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} - dev: false /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} diff --git a/be/algo-with-me-score/src/app.module.ts b/be/algo-with-me-score/src/app.module.ts index 1ee3997..c5b1813 100644 --- a/be/algo-with-me-score/src/app.module.ts +++ b/be/algo-with-me-score/src/app.module.ts @@ -2,6 +2,7 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { config } from 'dotenv'; import * as process from 'node:process'; @@ -15,6 +16,8 @@ import { Submission } from './score/entities/submission.entity'; import { User } from './score/entities/user.entity'; import { ScoreModule } from './score/score.module'; +config(); + @Module({ imports: [ ConfigModule.forRoot({ @@ -46,7 +49,7 @@ import { ScoreModule } from './score/score.module'; }, }), BullModule.registerQueue({ - name: process.env.REDIS_MESSAGE_QUEUE_NAME, + name: 'submission', }), ScoreModule, ], diff --git a/be/algo-with-me-score/src/score/score.module.ts b/be/algo-with-me-score/src/score/score.module.ts index aafa4a3..2c23bae 100644 --- a/be/algo-with-me-score/src/score/score.module.ts +++ b/be/algo-with-me-score/src/score/score.module.ts @@ -1,4 +1,3 @@ -import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; @@ -22,9 +21,6 @@ import { ScoreService } from './services/score.service'; CompetitionProblem, CompetitionParticipant, ]), - BullModule.registerQueue({ - name: process.env.REDIS_MESSAGE_QUEUE_NAME, - }), ], controllers: [], providers: [SubmissionConsumer, FilesystemService, ScoreService], diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index a54e76a..e599854 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -20,8 +20,9 @@ export class SubmissionConsumer { private readonly scoreService: ScoreService, ) {} - @Process('score') + @Process() async getMessageQueue(job: Job) { + console.log('test'); const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.socketId); const { socketId, submissionId } = messageQueueItem; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); From 2bfa7e4b83d1c2fdf41f6d97212f30caa7c70de4 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:40:18 +0900 Subject: [PATCH 133/226] =?UTF-8?q?fix:=20=ED=97=A4=EB=8D=94=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=86=A0=ED=81=B0=20=EB=B0=9B=EB=8D=98=EA=B1=B0=20?= =?UTF-8?q?auth=EC=97=90=EC=84=9C=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index fde2f2d..d8dc226 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -44,7 +44,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { @ConnectedSocket() client: Socket, ): Promise > { const authTokenPayloadDto: AuthTokenPayloadDto = this.authService.verifyToken( - client.handshake.headers.authorization, + client.handshake.auth.token, ); const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); const testcaseNum: number = await this.problemService.getProblenTestcaseNum( @@ -67,7 +67,7 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { // const user: User = await this.userService.getByEmail(authTokenPayloadDto.sub); // await this.competitionService.isUserJoinedCompetition(Number(competitionId), user.id); client.join(competitionId); - console.log(client.id); + console.log(client.id, client.handshake.auth); console.log(competitionId, args); } catch (error) { client.emit('messages', { message: `${error.message}` }); From 37685f6f922651ce41178e3bc9929096b02dc8ea Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 24 Nov 2023 19:37:22 +0900 Subject: [PATCH 134/226] =?UTF-8?q?fix:=20=EB=94=94=EB=A0=89=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=95=88=EB=A7=8C=EB=93=A4=EC=96=B4=EC=84=9C=20?= =?UTF-8?q?=ED=84=B0=EC=A7=80=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EA=B3=A0?= =?UTF-8?q?=EC=B9=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/app.service.ts | 2 +- .../src/score/services/filesystem.service.ts | 5 ++-- .../src/score/services/score.consumer.ts | 9 +++++--- .../src/score/services/score.service.ts | 23 +++++++++++++++++-- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/be/algo-with-me-score/src/app.service.ts b/be/algo-with-me-score/src/app.service.ts index e67dd89..73c32fd 100644 --- a/be/algo-with-me-score/src/app.service.ts +++ b/be/algo-with-me-score/src/app.service.ts @@ -9,6 +9,6 @@ export class AppService { constructor(@InjectQueue('submission') private testQueue: Queue) {} async addMessageQueue(item: MessageQueueItemDto) { - return await this.testQueue.add('score', item); + return await this.testQueue.add(item); } } diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 2239846..e16a91b 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -1,4 +1,4 @@ -import { InternalServerErrorException, Logger } from '@nestjs/common'; +import { Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; @@ -22,8 +22,7 @@ export class FilesystemService { const baseDirectory = `${submissionPath}/${competitionId}/${userId}/`; if (!fs.existsSync(baseDirectory)) { - new Logger().error(`파일시스템에 ${baseDirectory} 경로가 존재하지 않습니다`); - throw new InternalServerErrorException(); + fs.mkdirSync(baseDirectory, { recursive: true }); } fs.writeFileSync(path.join(baseDirectory, `${problemId}.js`), mergedCode); diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index e599854..1cd372d 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -22,7 +22,9 @@ export class SubmissionConsumer { @Process() async getMessageQueue(job: Job) { - console.log('test'); + const logger = new Logger(); + + logger.debug(`Redis로부터 제출 요청 받음: ${JSON.stringify(job.data)}`); const messageQueueItem = new MessageQueueItemDto(job.data.submissionId, job.data.socketId); const { socketId, submissionId } = messageQueueItem; const submission: Submission = await this.submissionRepository.findOneBy({ id: submissionId }); @@ -33,8 +35,9 @@ export class SubmissionConsumer { const problemId = submission.problemId; const competitionId = submission.competitionId; - // TODO: userId 가져오기 - const userId = 1; + const userId = submission.userId; + + logger.debug(`채점 시작: ${JSON.stringify({ competitionId, userId, problemId })}`); await this.filesystemService.writeSubmittedCode( submission.code, diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 606cd8e..33d0423 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -49,8 +49,27 @@ export class ScoreService { const testcaseAnswer = this.getTestcaseAnswer(problemId, testcaseId); const judgeResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); - await this.sendScoreResult( - new ScoreResultDto(submissionId, testcaseId, socketId, judgeResult, stdout, stderr, 0, 0), + const scoreResult = new ScoreResultDto( + submissionId, + testcaseId, + socketId, + judgeResult, + stdout, + stderr, + 0, + 0, + ); + await this.sendScoreResult(scoreResult); + const logger = new Logger(); + logger.debug( + `채점 완료: ${JSON.stringify({ + submissionId: scoreResult.submissionId, + competitionId, + userId, + problemId, + testcaseId, + judgeResult, + })}`, ); } From 8abf3f42822f888a9350bfc38e60a55a2b40b283 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:01:34 +0900 Subject: [PATCH 135/226] =?UTF-8?q?fix:=20=EC=B1=84=EC=A0=90=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EB=B0=9B=EB=8A=94=20api=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/controllers/competition.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index eabcd64..2060fca 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -101,6 +101,7 @@ export class CompetitionController { }) @UsePipes(new ValidationPipe({ transform: true })) async saveScoreResult(@Body() scoreResultDto: ScoreResultDto) { + console.log('채점완료 api', scoreResultDto); await this.competitionService.saveScoreResult(scoreResultDto); } From 4968a2413723d9d102c2eb832f1bcbc067792af2 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:11:25 +0900 Subject: [PATCH 136/226] =?UTF-8?q?fix:=20api=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=EB=A1=9C=20=EC=9A=94=EC=B2=AD=ED=95=98=EB=8A=94=20url=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 33d0423..d812ef4 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -79,6 +79,7 @@ export class ScoreService { process.env.API_SERVER_PORT, ]; const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; + console.log(url); try { await fetch(url, { method: 'POST', From 6f0718d9c71b2546141dd3ba58a710f89f6e6ab1 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:13:06 +0900 Subject: [PATCH 137/226] =?UTF-8?q?fix:=20api=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=9A=94=EC=B3=A5=20=EA=B2=B0=EA=B3=BC=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index d812ef4..10f112e 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -81,10 +81,11 @@ export class ScoreService { const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; console.log(url); try { - await fetch(url, { + const result = await fetch(url, { method: 'POST', body: JSON.stringify(scoreResult), }); + console.log(result.status); } catch (error) { new Logger().error( `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, From 780b046cab6bcc0e3f83031c5386b2ae4046c9b5 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:18:13 +0900 Subject: [PATCH 138/226] =?UTF-8?q?fix:=20api=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=EA=B2=B0=EA=B3=BC=EA=B0=92=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 10f112e..33efbec 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -85,7 +85,7 @@ export class ScoreService { method: 'POST', body: JSON.stringify(scoreResult), }); - console.log(result.status); + console.log(result.status, await result.json()); } catch (error) { new Logger().error( `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, From 599044a76e5ad4ef23d24c824916476b79063cd7 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:21:13 +0900 Subject: [PATCH 139/226] =?UTF-8?q?fix:=20api=20=EC=84=9C=EB=B2=84=20fetch?= =?UTF-8?q?=20header=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 33efbec..0e719d6 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -83,6 +83,9 @@ export class ScoreService { try { const result = await fetch(url, { method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify(scoreResult), }); console.log(result.status, await result.json()); From b768c2b5a23fe366bad0724b626230b1946b7ef8 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:24:15 +0900 Subject: [PATCH 140/226] =?UTF-8?q?fix:=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/competition/dto/score-result.dto.ts | 2 +- be/algo-with-me-score/src/score/entities/competition.enums.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts index 0c1e3b9..fa07c91 100644 --- a/be/algo-with-me-api/src/competition/dto/score-result.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/score-result.dto.ts @@ -21,5 +21,5 @@ export class ScoreResultDto { result: string; @ApiProperty() - stdOut: string; + stdout: string; } diff --git a/be/algo-with-me-score/src/score/entities/competition.enums.ts b/be/algo-with-me-score/src/score/entities/competition.enums.ts index 03836b2..6c75029 100644 --- a/be/algo-with-me-score/src/score/entities/competition.enums.ts +++ b/be/algo-with-me-score/src/score/entities/competition.enums.ts @@ -1,7 +1,7 @@ export const RESULT = { PROGRESS: '처리중', CORRECT: '정답입니다', - WRONG: '오답입니다.', + WRONG: '오답입니다', TIMEOUT: '시간초과', OOM: '메모리초과', } as const; From 1c240ced636f33dd88749a2ac14be074daf2bc6a Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 24 Nov 2023 21:33:57 +0900 Subject: [PATCH 141/226] =?UTF-8?q?fix:=20=EC=B1=84=EC=A0=90=EC=84=9C?= =?UTF-8?q?=EB=B2=84->API=20=EC=84=9C=EB=B2=84=EC=97=90=EA=B2=8C=20result?= =?UTF-8?q?=20=EC=A0=84=EB=8B=AC=ED=95=98=EB=8A=94=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/dtos/score-result.dto.ts | 6 ++---- .../src/score/services/score.service.ts | 11 +++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts index 9af8383..3a1ea11 100644 --- a/be/algo-with-me-score/src/score/dtos/score-result.dto.ts +++ b/be/algo-with-me-score/src/score/dtos/score-result.dto.ts @@ -1,11 +1,9 @@ -import { RESULT } from '../entities/competition.enums'; - export class ScoreResultDto { constructor( submissionId: number, testcaseId: number, socketId: string, - result: keyof typeof RESULT, + result: '처리중' | '정답입니다' | '오답입니다' | '시간초과' | '메모리초과', stdout: string, stderr: string, timeUsage: number, @@ -24,7 +22,7 @@ export class ScoreResultDto { submissionId: number; testcaseId: number; socketId: string; - result: keyof typeof RESULT; + result: '처리중' | '정답입니다' | '오답입니다' | '시간초과' | '메모리초과'; stdout: string; stderr: string; timeUsage: number; diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 0e719d6..cd693c8 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -3,7 +3,6 @@ import { InternalServerErrorException, Logger } from '@nestjs/common'; import * as fs from 'node:fs'; import { ScoreResultDto } from '../dtos/score-result.dto'; -import { RESULT } from '../entities/competition.enums'; import { Submission } from '../entities/submission.entity'; import ICoderunResponse from '../interfaces/coderun-response.interface'; @@ -166,15 +165,15 @@ export class ScoreService { codeRunResponse: ICoderunResponse, codeRunOutput: string, testcaseAnswer: string, - ): keyof typeof RESULT { + ): '처리중' | '정답입니다' | '오답입니다' | '시간초과' | '메모리초과' { if (codeRunResponse.result === 'TIMEOUT') { - return 'TIMEOUT'; + return '시간초과'; } else if (codeRunResponse.result !== 'SUCCESS') { - return 'WRONG'; + return '오답입니다'; } else if (codeRunOutput === testcaseAnswer) { - return 'CORRECT'; + return '정답입니다'; } else { - return 'WRONG'; + return '오답입니다'; } } } From 0d394dcb9acaa4295a84c02374155f28ad3ef3c5 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 24 Nov 2023 21:43:40 +0900 Subject: [PATCH 142/226] =?UTF-8?q?chore:=20scoreResult=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B0=8D=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index cd693c8..f002455 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -78,7 +78,7 @@ export class ScoreService { process.env.API_SERVER_PORT, ]; const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; - console.log(url); + console.log(JSON.stringify(scoreResult)); try { const result = await fetch(url, { method: 'POST', From f1243d0b1bdf3983303f6100c3206a6f4d7cad64 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Fri, 24 Nov 2023 21:51:22 +0900 Subject: [PATCH 143/226] =?UTF-8?q?fix:=20scoreResult=20unexpected=20end?= =?UTF-8?q?=20of=20input=20JSON=20=EC=98=A4=EB=A5=98=20=EC=97=86=EC=95=A0?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index f002455..48dab7e 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -87,7 +87,7 @@ export class ScoreService { }, body: JSON.stringify(scoreResult), }); - console.log(result.status, await result.json()); + console.log(result.status); } catch (error) { new Logger().error( `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, From f7d3b0c8c2b4621f29f18bbbe5d7140d87005a2e Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:19:13 +0900 Subject: [PATCH 144/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20dto=20?= =?UTF-8?q?=EC=8A=A4=ED=8E=A0=EB=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/services/competition.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 7ebc616..931bb02 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -175,7 +175,7 @@ export class CompetitionService { this.submissionRepository.save(submission); result['problemId'] = submission.problemId; - result['stdOut'] = scoreResultDto.stdOut; + result['stdout'] = scoreResultDto.stdout; this.server.to(scoreResultDto.socketId).emit('scoreResult', result); } From da6f945d3d88cff85ec8d1dbcee05eb260b42504 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:25:16 +0900 Subject: [PATCH 145/226] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=A0=84=EC=86=A1=ED=95=98=EB=8A=94=20=EC=86=8C?= =?UTF-8?q?=EC=BC=93=20message=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/gateways/competition.gateway.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts index d8dc226..471b943 100644 --- a/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts +++ b/be/algo-with-me-api/src/competition/gateways/competition.gateway.ts @@ -57,6 +57,11 @@ export class CompetitionGateWay implements OnGatewayConnection, OnGatewayInit { return { event, data }; } + @SubscribeMessage('ping') + async handlePing(@ConnectedSocket() client: Socket) { + client.emit('ping', new Date()); + } + public async handleConnection(client: Socket, ...args: any[]) { try { const { competitionId } = client.handshake.query; From 313dd120ceaa9951c25f27062d61d36ccdc27ee2 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 28 Nov 2023 19:34:17 +0900 Subject: [PATCH 146/226] =?UTF-8?q?feat:=20docker=20container=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=8B=9C=EA=B0=84=EA=B3=BC=20=EB=A9=94=EB=AA=A8?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AC=EC=9A=A9=EB=9F=89=EC=9D=84=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20=EC=93=B0=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/Dockerfile | 6 ++++-- be/algo-with-me-docker/node-sh/run.sh | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index 929600f..429767b 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -2,17 +2,19 @@ ARG NODE_VERSION=20.9.0 ARG ALPINE_VERSION=3.18 ARG PORT=5000 -FROM node:${NODE_VERSION}-alpine AS base +FROM node:${NODE_VERSION}-bookworm AS base CMD "mkdir -p /algo-with-me" WORKDIR /algo-with-me COPY --chmod=555 ./node-sh /algo-with-me/node-sh COPY . /algo-with-me -RUN apk update \ +RUN apt update \ && npm install -g pnpm \ && pnpm install +SHELL ["/bin/bash", "-ec"] + EXPOSE ${PORT} CMD node dist/app.js diff --git a/be/algo-with-me-docker/node-sh/run.sh b/be/algo-with-me-docker/node-sh/run.sh index efd8401..fc08596 100755 --- a/be/algo-with-me-docker/node-sh/run.sh +++ b/be/algo-with-me-docker/node-sh/run.sh @@ -11,7 +11,7 @@ mkdir -p "/algo-with-me/submissions/$1/$2/" || exit 1 SUBMISSION_JS_FILE="/algo-with-me/submissions/$1/$2/$3.js" -DETAIL_FILE="/algo-with-me/submissions/$1/$2/$3.$4.detail" +MEMORY_FILE="/algo-with-me/submissions/$1/$2/$3.$4.memory" # 제출된 js 파일이 있으면 node로 js 파일 실행 # 주의: judge.sh와 run.sh는 execute 권한이 부여되어야 함 @@ -22,8 +22,7 @@ if [ -f "$SUBMISSION_JS_FILE" ]; then # U Total number of CPU-seconds that the process used directly (in user mode), in seconds. # e Elapsed real (wall clock) time used by the process, in seconds. # M Maximum resident set size of the process during its lifetime, in Kilobytes. - # /usr/bin/time -o "$DETAIL_FILE" -f "%e %M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" "$4" - /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" "$4" || exit 2 + /algo-with-me/node-sh/time -o "$MEMORY_FILE" -f "%M" /algo-with-me/node-sh/runJs.sh "$1" "$2" "$3" "$4" || exit 2 echo "[algo-with-me] run.sh: successfully ran $SUBMISSION_JS_FILE. COMPETITION_ID=$1, USER_ID=$2, PROBLEM_ID=$3, TESTCASE_ID=$4" else echo "[algo-with-me] run.sh: cannot find submitted js file $SUBMISSION_JS_FILE" From 1b11f2cae72fd8f5a71ae50dca4ceaf18b35d91f Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 28 Nov 2023 19:41:48 +0900 Subject: [PATCH 147/226] =?UTF-8?q?feat:=20=ED=94=84=EB=A0=88=EC=9E=84=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=97=90=EC=84=9C=20=EB=A9=94=EB=AA=A8?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AC=EC=9A=A9=EB=9F=89=EC=9D=84=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20=EC=93=B0=EC=A7=80=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-docker/node-sh/runJs.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/be/algo-with-me-docker/node-sh/runJs.sh b/be/algo-with-me-docker/node-sh/runJs.sh index 29199d1..9fa4789 100755 --- a/be/algo-with-me-docker/node-sh/runJs.sh +++ b/be/algo-with-me-docker/node-sh/runJs.sh @@ -15,9 +15,8 @@ STDOUT_FILE="/algo-with-me/submissions/$1/$2/$3.$4.stdout" STDERR_FILE="/algo-with-me/submissions/$1/$2/$3.$4.stderr" RESULT_FILE="/algo-with-me/submissions/$1/$2/$3.$4.result" TIME_FILE="/algo-with-me/submissions/$1/$2/$3.$4.time" -MEMORY_FILE="/algo-with-me/submissions/$1/$2/$3.$4.memory" TESTCASE_INPUTFILE="/algo-with-me/testcases/$3/secrets/$4.in" -node "$SUBMISSION_JS_FILE" "$TESTCASE_INPUTFILE" "$RESULT_FILE" "$TIME_FILE" "$MEMORY_FILE" 1> "$STDOUT_FILE" 2> "$STDERR_FILE" +node "$SUBMISSION_JS_FILE" "$TESTCASE_INPUTFILE" "$RESULT_FILE" "$TIME_FILE" 1> "$STDOUT_FILE" 2> "$STDERR_FILE" exit 0 From 879ac628b78aee0eb067d30963deba379da84719 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 28 Nov 2023 20:12:50 +0900 Subject: [PATCH 148/226] =?UTF-8?q?fix:=20a+b=20=EC=98=A4=EB=8B=B5=20?= =?UTF-8?q?=EB=82=98=EC=98=A4=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(https://www.notion.so/A-B-84cd187171d840d6a8e4b183?= =?UTF-8?q?8cf7194b=3Fpvs=3D4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-score/src/score/services/score.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 48dab7e..f81ad8e 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -158,7 +158,7 @@ export class ScoreService { throw new InternalServerErrorException(); } - return fs.readFileSync(filepath).toString(); + return fs.readFileSync(filepath).toString().trim(); } private judge( @@ -166,6 +166,11 @@ export class ScoreService { codeRunOutput: string, testcaseAnswer: string, ): '처리중' | '정답입니다' | '오답입니다' | '시간초과' | '메모리초과' { + new Logger().debug( + `실행 결과: ${ + codeRunResponse.result + }, 제출한 답안: ${codeRunOutput}(${typeof codeRunOutput}), 정답: ${testcaseAnswer}(${typeof testcaseAnswer})`, + ); if (codeRunResponse.result === 'TIMEOUT') { return '시간초과'; } else if (codeRunResponse.result !== 'SUCCESS') { From 67f28845ba6bf00821d976271819b9d7e610e8c3 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Tue, 28 Nov 2023 20:20:18 +0900 Subject: [PATCH 149/226] =?UTF-8?q?fix:=20memory,=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EB=9F=89=20=EC=A0=84=EB=8B=AC=ED=95=B4?= =?UTF-8?q?=EC=A3=BC=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/score.service.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index f81ad8e..1f66f18 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -44,6 +44,8 @@ export class ScoreService { result: codeRunOutput, stdout, stderr, + timeUsage, + memoryUsage, } = this.getCodeRunOutputs(competitionId, userId, problemId, testcaseId); const testcaseAnswer = this.getTestcaseAnswer(problemId, testcaseId); const judgeResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); @@ -55,8 +57,8 @@ export class ScoreService { judgeResult, stdout, stderr, - 0, - 0, + timeUsage, + memoryUsage, ); await this.sendScoreResult(scoreResult); const logger = new Logger(); @@ -123,18 +125,23 @@ export class ScoreService { userId: number, problemId: number, testcaseId: number, - ): { result: string; stdout: string; stderr: string } { + ): { result: string; stdout: string; stderr: string; timeUsage: number; memoryUsage: number } { const submissionPath = process.env.SUBMISSION_PATH; const submissionBaseFilename = `${submissionPath}/${competitionId}/${userId}/${problemId}.${testcaseId}`; - const [resultFilepath, stdoutFilepath, stderrFilepath] = [ - `${submissionBaseFilename}.result`, - `${submissionBaseFilename}.stdout`, - `${submissionBaseFilename}.stderr`, - ]; + const [resultFilepath, stdoutFilepath, stderrFilepath, timeUsageFilepath, memoryUsageFilepath] = + [ + `${submissionBaseFilename}.result`, + `${submissionBaseFilename}.stdout`, + `${submissionBaseFilename}.stderr`, + `${submissionBaseFilename}.time`, + `${submissionBaseFilename}.memory`, + ]; if ( !fs.existsSync(resultFilepath) || !fs.existsSync(stdoutFilepath) || - !fs.existsSync(stderrFilepath) + !fs.existsSync(stderrFilepath) || + !fs.existsSync(timeUsageFilepath) || + !fs.existsSync(memoryUsageFilepath) ) { new Logger().error( `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`, @@ -142,12 +149,14 @@ export class ScoreService { throw new InternalServerErrorException(); } - const [result, stdout, stderr] = [ + const [result, stdout, stderr, timeUsage, memoryUsage] = [ fs.readFileSync(resultFilepath).toString(), fs.readFileSync(stdoutFilepath).toString(), fs.readFileSync(stderrFilepath).toString(), + parseInt(fs.readFileSync(timeUsageFilepath).toString()), + parseInt(fs.readFileSync(memoryUsageFilepath).toString()), ]; - return { result, stdout, stderr }; + return { result, stdout, stderr, timeUsage, memoryUsage }; } private getTestcaseAnswer(problemId: number, testcaseId: number) { From 1017ec7370d00b576a669404b832a09af331d7a6 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:12:04 +0900 Subject: [PATCH 150/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../competition/dto/create-competition.dto.ts | 26 +++++++++++++++---- .../services/competition.service.ts | 14 ++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index db7c91e..5a9b09a 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -1,5 +1,15 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty } from 'class-validator'; +import { + ArrayMaxSize, + ArrayMinSize, + ArrayUnique, + IsArray, + IsDateString, + IsInt, + IsNotEmpty, + Max, + Min, +} from 'class-validator'; import { Competition } from '../entities/competition.entity'; @@ -31,19 +41,25 @@ export class CreateCompetitionDto { detail: string; @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) - @IsNotEmpty() + @IsInt() + @Min(1) + @Max(200) maxParticipants: number; @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) - @IsNotEmpty() + @IsDateString() startsAt: Date; @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) - @IsNotEmpty() + @IsDateString() endsAt: Date; @ApiProperty({ description: '대회에 사용되는 문제 id 리스트' }) - @IsNotEmpty() + @IsArray() + @ArrayMinSize(1) + @ArrayMaxSize(30) + @ArrayUnique() + @IsInt({ each: true }) problemIds: number[]; toEntity(user: User): Competition { diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 931bb02..7e6503d 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -59,7 +59,7 @@ export class CompetitionService { } async create(createCompetitionDto: CreateCompetitionDto, user: User) { - this.assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto); + this.competitionTimeValidation(createCompetitionDto); const competitionProblems: CompetitionProblem[] = []; for (const problemId of createCompetitionDto.problemIds) { @@ -223,11 +223,15 @@ export class CompetitionService { return problem; } - private assertProblemIdsArrayLengthNotExceeds30(createCompetitionDto: CreateCompetitionDto) { - if (createCompetitionDto.problemIds.length > 30) { + private competitionTimeValidation(createCompetitionDto: CreateCompetitionDto) { + const FIVE_MINUTES = 5 * 60 * 1000; + const startsAt = new Date(createCompetitionDto.endsAt); + const endsAt = new Date(createCompetitionDto.startsAt); + if (startsAt.getTime() - endsAt.getTime() < FIVE_MINUTES) + throw new BadRequestException('대회 시간은 최소 5분 이상이어야 합니다.'); + if (startsAt.getTime() - new Date().getTime() < FIVE_MINUTES) throw new BadRequestException( - `정책 상 하나의 대회에서는 30개가 넘는 문제를 출제할 수 없습니다. (${createCompetitionDto.problemIds.length}개를 출제함)`, + '대회 시작 시간은 현재 시간으로부터 최소 5분 이후이어야 합니다.', ); - } } } From 96ca5fc9f19b5d50b6862c935e7a5f288a2331eb Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 29 Nov 2023 00:42:40 +0900 Subject: [PATCH 151/226] =?UTF-8?q?fix:=20update,=20create=20dto=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9,=20validation=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 12 ++--- .../competition/dto/create-competition.dto.ts | 20 ++++---- .../competition/dto/update-competition.dto.ts | 50 ------------------- 3 files changed, 15 insertions(+), 67 deletions(-) delete mode 100644 be/algo-with-me-api/src/competition/dto/update-competition.dto.ts diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index 2060fca..e32e4dc 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -14,10 +14,9 @@ import { AuthGuard } from '@nestjs/passport'; import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; -import { CreateCompetitionDto } from '../dto/create-competition.dto'; +import { CompetitionDto } from '../dto/create-competition.dto'; import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; -import { UpdateCompetitionDto } from '../dto/update-competition.dto'; import { CompetitionService } from '../services/competition.service'; import { CompetitionResponseDto } from '@src/competition/dto/competition.response.dto'; @@ -58,8 +57,8 @@ export class CompetitionController { @ApiBearerAuth() @UsePipes(new ValidationPipe({ transform: true })) @UseGuards(AuthGuard('jwt')) - create(@Body() createCompetitionDto: CreateCompetitionDto, @AuthUser() user: User) { - return this.competitionService.create(createCompetitionDto, user); + create(@Body() competitionDto: CompetitionDto, @AuthUser() user: User) { + return this.competitionService.create(competitionDto, user); } @Put('/:competitionId') @@ -67,16 +66,15 @@ export class CompetitionController { summary: '대회 정보 수정', description: `URL의 파라미터(\`/:id\`)로 주어진 대회 id에 해당하는 대회 정보를 수정한다. request JSON 중 **수정하기를 원하는 것만** key: value 형식으로 요청한다.`, }) - @ApiResponse({ type: Boolean }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt')) @UsePipes(new ValidationPipe({ transform: true })) update( @Param('competitionId') competitionId: number, - @Body() updateCompetitionDto: UpdateCompetitionDto, + @Body() competitionDto: CompetitionDto, @AuthUser() user: User, ) { - return this.competitionService.update(competitionId, updateCompetitionDto, user); + this.competitionService.update(competitionId, competitionDto, user); } @Get('/:competitionId/problems') diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts index 5a9b09a..d8ec769 100644 --- a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts @@ -15,13 +15,13 @@ import { Competition } from '../entities/competition.entity'; import { User } from '@src/user/entities/user.entity'; -export class CreateCompetitionDto { +export class CompetitionDto { constructor( name: string, detail: string, maxParticipants: number, - startsAt: Date, - endsAt: Date, + startsAt: string, + endsAt: string, problemIds: number[], ) { this.name = name; @@ -46,15 +46,15 @@ export class CreateCompetitionDto { @Max(200) maxParticipants: number; - @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) + @ApiProperty({ description: '대회 시작 일시 (ISO string)', type: Date }) @IsDateString() - startsAt: Date; + startsAt: string; - @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) + @ApiProperty({ description: '대회 종료 일시 (ISO string)', type: Date }) @IsDateString() - endsAt: Date; + endsAt: string; - @ApiProperty({ description: '대회에 사용되는 문제 id 리스트' }) + @ApiProperty({ description: '대회에 사용되는 문제 id 리스트', type: Number, isArray: true }) @IsArray() @ArrayMinSize(1) @ArrayMaxSize(30) @@ -67,8 +67,8 @@ export class CreateCompetitionDto { competition.name = this.name; competition.detail = this.detail; competition.maxParticipants = this.maxParticipants; - competition.startsAt = this.startsAt; - competition.endsAt = this.endsAt; + competition.startsAt = new Date(this.startsAt); + competition.endsAt = new Date(this.endsAt); competition.userId = user.id; return competition; } diff --git a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts deleted file mode 100644 index 1484843..0000000 --- a/be/algo-with-me-api/src/competition/dto/update-competition.dto.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Optional } from '@nestjs/common'; -import { ApiProperty } from '@nestjs/swagger'; - -import { Competition } from '@src/competition/entities/competition.entity'; - -export class UpdateCompetitionDto { - constructor( - name: string, - detail: string, - maxParticipants: number, - startsAt: Date, - endsAt: Date, - ) { - this.name = name; - this.detail = detail; - this.maxParticipants = maxParticipants; - this.startsAt = startsAt; - this.endsAt = endsAt; - } - - @ApiProperty({ description: '대회 이름' }) - @Optional() - name: string; - - @ApiProperty({ description: '대회에 대한 설명글' }) - @Optional() - detail: string; - - @ApiProperty({ description: '대회에 참여 가능한 최대 인원' }) - @Optional() - maxParticipants: number; - - @ApiProperty({ description: '대회 시작 일시 (ISO string)' }) - @Optional() - startsAt: Date; - - @ApiProperty({ description: '대회 종료 일시 (ISO string)' }) - @Optional() - endsAt: Date; - - toEntity(): Competition { - const competition = new Competition(); - competition.name = this.name; - competition.detail = this.detail; - competition.maxParticipants = this.maxParticipants; - competition.startsAt = new Date(this.startsAt); - competition.endsAt = new Date(this.endsAt); - return competition; - } -} From 4aa4615e1b1150c2df6de21af040d33025462410 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 29 Nov 2023 00:43:59 +0900 Subject: [PATCH 152/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1,=20=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80,=20=ED=8A=B8=EB=9E=9C?= =?UTF-8?q?=EC=9E=AD=EC=85=98=20=EC=88=98=EC=A0=95,=20dto=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/competition.controller.ts | 2 +- ...-competition.dto.ts => competition.dto.ts} | 0 .../services/competition.service.ts | 79 ++++++++++++------- 3 files changed, 50 insertions(+), 31 deletions(-) rename be/algo-with-me-api/src/competition/dto/{create-competition.dto.ts => competition.dto.ts} (100%) diff --git a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts index e32e4dc..a502ec8 100644 --- a/be/algo-with-me-api/src/competition/controllers/competition.controller.ts +++ b/be/algo-with-me-api/src/competition/controllers/competition.controller.ts @@ -13,8 +13,8 @@ import { import { AuthGuard } from '@nestjs/passport'; import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { CompetitionDto } from '../dto/competition.dto'; import { CompetitionProblemResponseDto } from '../dto/competition.problem.response.dto'; -import { CompetitionDto } from '../dto/create-competition.dto'; import { ProblemSimpleResponseDto } from '../dto/problem.simple.response.dto'; import { ScoreResultDto } from '../dto/score-result.dto'; import { CompetitionService } from '../services/competition.service'; diff --git a/be/algo-with-me-api/src/competition/dto/create-competition.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.dto.ts similarity index 100% rename from be/algo-with-me-api/src/competition/dto/create-competition.dto.ts rename to be/algo-with-me-api/src/competition/dto/competition.dto.ts diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index 7e6503d..cd6b2fb 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -22,16 +22,16 @@ import { CompetitionProblem } from '../entities/competition.problem.entity'; import { Problem } from '../entities/problem.entity'; import { Submission } from '../entities/submission.entity'; +import { CompetitionDto } from '@src/competition/dto/competition.dto'; import { CompetitionResponseDto } from '@src/competition/dto/competition.response.dto'; import { CompetitionSimpleResponseDto } from '@src/competition/dto/competition.simple-response.dto'; -import { CreateCompetitionDto } from '@src/competition/dto/create-competition.dto'; -import { UpdateCompetitionDto } from '@src/competition/dto/update-competition.dto'; import { Competition } from '@src/competition/entities/competition.entity'; import { User } from '@src/user/entities/user.entity'; @Injectable() export class CompetitionService { server: Server; + FIVE_MINUTES: number = 5 * 60 * 1000; constructor( @InjectRepository(Competition) private readonly competitionRepository: Repository , @InjectRepository(Problem) private readonly problemRepository: Repository , @@ -58,43 +58,70 @@ export class CompetitionService { return CompetitionResponseDto.from(competition); } - async create(createCompetitionDto: CreateCompetitionDto, user: User) { - this.competitionTimeValidation(createCompetitionDto); + async create(competitionDto: CompetitionDto, user: User) { + this.competitionTimeValidation(competitionDto); const competitionProblems: CompetitionProblem[] = []; - for (const problemId of createCompetitionDto.problemIds) { - const problem = await this.assertProblemExistsInDb(problemId, createCompetitionDto); + for (const problemId of competitionDto.problemIds) { + const problem = await this.assertProblemExistsInDb(problemId); const competitionProblem = new CompetitionProblem(); competitionProblem.problem = problem; competitionProblems.push(competitionProblem); } - const competition: Competition = createCompetitionDto.toEntity(user); + const competition: Competition = competitionDto.toEntity(user); const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { - const savedCompetition: Competition = await this.competitionRepository.save(competition, { - transaction: false, - }); - for (const competitionProblem of competitionProblems) { - competitionProblem.competition = savedCompetition; - await this.competitionProblemRepository.save(competitionProblem, { transaction: false }); - } + const savedCompetition: Competition = await queryRunner.manager.save(competition); + competitionProblems.forEach( + (element: CompetitionProblem) => (element.competition = savedCompetition), + ); + queryRunner.manager.save(competitionProblems); await queryRunner.commitTransaction(); + await queryRunner.release(); } catch (error) { await queryRunner.rollbackTransaction(); + await queryRunner.release(); throw error; } } - async update(id: number, updateCompetitionDto: UpdateCompetitionDto, user: User) { + async update(id: number, competitionDto: CompetitionDto, user: User) { const competition: Competition = await this.competitionRepository.findOneBy({ id }); + if (!competition) throw new NotFoundException('대회를 찾을 수 없습니다.'); if (competition.userId !== user.id) throw new UnauthorizedException('대회 주최자가 아닙니다.'); - const result = await this.competitionRepository.update({ id: id }, { ...updateCompetitionDto }); - return !!result.affected; + console.log(competition.startsAt.toString(), new Date().toString()); + if (competition.startsAt.getTime() - new Date().getTime() < this.FIVE_MINUTES) + throw new BadRequestException('대회 시작 5분 전 부터는 수정이 불가능합니다.'); + this.competitionTimeValidation(competitionDto); + + const competitionProblems: CompetitionProblem[] = []; + competitionDto.problemIds.forEach(async (element: number) => { + const problem: Problem = await this.assertProblemExistsInDb(element); + const competitionProblem: CompetitionProblem = new CompetitionProblem(); + competitionProblem.competitionId = competition.id; + competitionProblem.problem = problem; + competitionProblems.push(competitionProblem); + }); + + const queryRunner = this.dataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { + await queryRunner.manager.delete(CompetitionProblem, { competitionId: competition.id }); + await queryRunner.manager.update(Competition, competition.id, competitionDto.toEntity(user)); + await queryRunner.manager.save(competitionProblems); + await queryRunner.commitTransaction(); + } catch (error) { + await queryRunner.rollbackTransaction(); + await queryRunner.release(); + throw error; + } } async findOneProblem(id: number) { @@ -208,28 +235,20 @@ export class CompetitionService { ); } - private async assertProblemExistsInDb( - problemId: number, - createCompetitionDto: CreateCompetitionDto, - ) { + private async assertProblemExistsInDb(problemId: number) { const problem = await this.problemRepository.findOneBy({ id: problemId }); if (!problem) { - throw new NotFoundException( - `대회를 생성할 때 주어진 문제 리스트 ${JSON.stringify( - createCompetitionDto.problemIds, - )}에 존재하지 않는 문제가 있습니다 (id: ${problemId})`, - ); + throw new NotFoundException(`존재하지 않는 문제가 있습니다 (problemId: ${problemId})`); } return problem; } - private competitionTimeValidation(createCompetitionDto: CreateCompetitionDto) { - const FIVE_MINUTES = 5 * 60 * 1000; + private competitionTimeValidation(createCompetitionDto: CompetitionDto) { const startsAt = new Date(createCompetitionDto.endsAt); const endsAt = new Date(createCompetitionDto.startsAt); - if (startsAt.getTime() - endsAt.getTime() < FIVE_MINUTES) + if (startsAt.getTime() - endsAt.getTime() < this.FIVE_MINUTES) throw new BadRequestException('대회 시간은 최소 5분 이상이어야 합니다.'); - if (startsAt.getTime() - new Date().getTime() < FIVE_MINUTES) + if (startsAt.getTime() - new Date().getTime() < this.FIVE_MINUTES) throw new BadRequestException( '대회 시작 시간은 현재 시간으로부터 최소 5분 이후이어야 합니다.', ); From d54467839f2fb0202cf9d90035e374d31ff1f1d0 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 29 Nov 2023 01:25:55 +0900 Subject: [PATCH 153/226] =?UTF-8?q?fix:=20=EB=8C=80=ED=9A=8C=20=EC=84=B8?= =?UTF-8?q?=EB=B6=80=20=EC=A1=B0=ED=9A=8C=EC=97=90=20=EC=A3=BC=EC=B5=9C?= =?UTF-8?q?=EC=9E=90,=20=EC=B0=B8=EC=97=AC=EC=9E=90=20=EC=9D=B4=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/competition.response.dto.ts | 42 +++++++++++-------- .../services/competition.service.ts | 38 +++++++++++++++-- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts index 7bbbaec..867845e 100644 --- a/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts +++ b/be/algo-with-me-api/src/competition/dto/competition.response.dto.ts @@ -1,9 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty } from 'class-validator'; +import { Competition } from '../entities/competition.entity'; + export class CompetitionResponseDto { constructor( id: number, + host: string, + participants: string[], name: string, detail: string, maxParticipants: number, @@ -13,6 +17,8 @@ export class CompetitionResponseDto { updatedAt: string, ) { this.id = id; + this.host = host; + this.participants = participants; this.name = name; this.detail = detail; this.maxParticipants = maxParticipants; @@ -26,6 +32,13 @@ export class CompetitionResponseDto { @IsNotEmpty() id: number; + @ApiProperty({ description: '주최자 이메일' }) + @IsNotEmpty() + host: string; + + @ApiProperty({ description: '참가자 이메일' }) + participants: string[]; + @ApiProperty({ description: '대회 이름' }) @IsNotEmpty() name: string; @@ -58,25 +71,18 @@ export class CompetitionResponseDto { @IsNotEmpty() updatedAt: string; - static from(args: { - id: number; - name: string; - detail: string; - maxParticipants: number; - startsAt: Date; - endsAt: Date; - createdAt: Date; - updatedAt: Date; - }) { + static from(competition: Competition, host: string, competitionParticipants: string[]) { return new CompetitionResponseDto( - args.id, - args.name, - args.detail, - args.maxParticipants, - args.startsAt.toISOString(), - args.endsAt.toISOString(), - args.createdAt.toISOString(), - args.updatedAt.toISOString(), + competition.id, + host, + competitionParticipants, + competition.name, + competition.detail, + competition.maxParticipants, + competition.startsAt.toISOString(), + competition.endsAt.toISOString(), + competition.createdAt.toISOString(), + competition.updatedAt.toISOString(), ); } } diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index cd6b2fb..b419ed5 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -53,9 +53,41 @@ export class CompetitionService { } async findOne(competitionId: number) { - const competition = await this.competitionRepository.findOneBy({ id: competitionId }); - this.assertCompetitionExists(competition); - return CompetitionResponseDto.from(competition); + const competitions = await this.competitionRepository.find({ + select: { + user: { + email: true, + }, + }, + where: { id: competitionId }, + relations: { + user: true, + }, + }); + if (competitions.length !== 1) + throw new NotFoundException( + `대회 id ${competitionId}에 해당하는 대회 정보를 찾을 수 없습니다`, + ); + const competition = competitions.shift(); + const competitionParticipants: CompetitionParticipant[] = + await this.competitionParticipantRepository.find({ + select: { + user: { + email: true, + }, + }, + where: { + competitionId: competitionId, + }, + relations: { + user: true, + }, + }); + return CompetitionResponseDto.from( + competition, + competition.user.email, + competitionParticipants.map((element: CompetitionParticipant) => element.user.email), + ); } async create(competitionDto: CompetitionDto, user: User) { From dd36c1f7c7782e654d000bd3b49bac0213c7eee3 Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 29 Nov 2023 01:26:48 +0900 Subject: [PATCH 154/226] =?UTF-8?q?fix:=20type=20hint=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/competition/services/competition.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/be/algo-with-me-api/src/competition/services/competition.service.ts b/be/algo-with-me-api/src/competition/services/competition.service.ts index b419ed5..559f5b3 100644 --- a/be/algo-with-me-api/src/competition/services/competition.service.ts +++ b/be/algo-with-me-api/src/competition/services/competition.service.ts @@ -53,7 +53,7 @@ export class CompetitionService { } async findOne(competitionId: number) { - const competitions = await this.competitionRepository.find({ + const competitions: Competition[] = await this.competitionRepository.find({ select: { user: { email: true, @@ -68,7 +68,7 @@ export class CompetitionService { throw new NotFoundException( `대회 id ${competitionId}에 해당하는 대회 정보를 찾을 수 없습니다`, ); - const competition = competitions.shift(); + const competition: Competition = competitions.shift(); const competitionParticipants: CompetitionParticipant[] = await this.competitionParticipantRepository.find({ select: { From dc03ae9c7ff88f57ce966fd4ce101f07e13649ba Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 29 Nov 2023 15:08:11 +0900 Subject: [PATCH 155/226] =?UTF-8?q?chore:=20docker=20build,=20run=20?= =?UTF-8?q?=EC=89=98=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/build-api | 5 +++++ be/build-docker | 5 +++++ be/build-score | 5 +++++ be/run-api | 8 ++++++++ be/run-docker | 14 ++++++++++++++ be/run-score | 8 ++++++++ 6 files changed, 45 insertions(+) create mode 100644 be/build-api create mode 100644 be/build-docker create mode 100644 be/build-score create mode 100644 be/run-api create mode 100644 be/run-docker create mode 100644 be/run-score diff --git a/be/build-api b/be/build-api new file mode 100644 index 0000000..dd32af5 --- /dev/null +++ b/be/build-api @@ -0,0 +1,5 @@ +sudo docker container stop algo-with-me-api 2>/dev/null +sudo docker container rm algo-with-me-api 2>/dev/null +sudo docker image rm algo-with-me-api 2>/dev/null +cd algo-with-me-api/ +sudo docker build -t algo-with-me-api . \ No newline at end of file diff --git a/be/build-docker b/be/build-docker new file mode 100644 index 0000000..1a1b194 --- /dev/null +++ b/be/build-docker @@ -0,0 +1,5 @@ +sudo docker container stop algo-with-me-docker 2>/dev/null +sudo docker container rm algo-with-me-docker 2>/dev/null +sudo docker image rm algo-with-me-docker 2>/dev/null +cd algo-with-me-docker/ +sudo docker build -t algo-with-me-docker . \ No newline at end of file diff --git a/be/build-score b/be/build-score new file mode 100644 index 0000000..c7edcf3 --- /dev/null +++ b/be/build-score @@ -0,0 +1,5 @@ +sudo docker container stop algo-with-me-score 2>/dev/null +sudo docker container rm algo-with-me-score 2>/dev/null +sudo docker image rm algo-with-me-score 2>/dev/null +cd algo-with-me-score/ +sudo docker build -t algo-with-me-score . \ No newline at end of file diff --git a/be/run-api b/be/run-api new file mode 100644 index 0000000..dfd8e53 --- /dev/null +++ b/be/run-api @@ -0,0 +1,8 @@ +sudo docker container stop algo-with-me-api 2>/dev/null +sudo docker container rm algo-with-me-api 2>/dev/null +sudo docker run -d -p 3000:3000 \ +-v /home/be/algo-with-me/problems/:/algo-with-me/problems/ \ +-v /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ \ +-v /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ \ +--name algo-with-me-api \ +algo-with-me-api diff --git a/be/run-docker b/be/run-docker new file mode 100644 index 0000000..65cef9c --- /dev/null +++ b/be/run-docker @@ -0,0 +1,14 @@ +sudo docker container stop algo-with-me-docker 2>/dev/null +sudo docker container rm algo-with-me-docker 2>/dev/null +sudo docker run -d \ +-p 5000:5000 \ +-e COMPETITION_ID=$1 \ +-e USER_ID=$2 \ +-e PROBLEM_ID=$3 \ +-e TESTCASE_ID=$4 \ +-v /home/be/algo-with-me/problems:/algo-with-me/problems \ +-v /home/be/algo-with-me/testcases:/algo-with-me/testcases \ +-v /home/be/algo-with-me/submissions:/algo-with-me/submissions \ +--user "$(id -u)":"$(id -g)" \ +--name algo-with-me-docker \ +algo-with-me-docker \ No newline at end of file diff --git a/be/run-score b/be/run-score new file mode 100644 index 0000000..5cb15ae --- /dev/null +++ b/be/run-score @@ -0,0 +1,8 @@ +sudo docker container stop algo-with-me-score 2>/dev/null +sudo docker container rm algo-with-me-score 2>/dev/null +sudo docker run -d -p 4000:4000 \ +-v /home/be/algo-with-me/problems/:/algo-with-me/problems/ \ +-v /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ \ +-v /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ \ +--name algo-with-me-score \ +algo-with-me-score \ No newline at end of file From 636921f6cb1935a9b4e2288e89d7087b62cfdba3 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 29 Nov 2023 16:32:46 +0900 Subject: [PATCH 156/226] =?UTF-8?q?feat:=20docker=20compose=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=20=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/build-api | 0 be/build-compose | 21 +++++++++++++++++++++ be/build-docker | 0 be/build-score | 0 be/docker-compose.yaml | 31 +++++++++++++++++++++++++++++++ be/run-api | 0 be/run-compose | 1 + be/run-compose-background | 1 + be/run-docker | 0 be/run-score | 0 10 files changed, 54 insertions(+) mode change 100644 => 100755 be/build-api create mode 100755 be/build-compose mode change 100644 => 100755 be/build-docker mode change 100644 => 100755 be/build-score create mode 100644 be/docker-compose.yaml mode change 100644 => 100755 be/run-api create mode 100755 be/run-compose create mode 100755 be/run-compose-background mode change 100644 => 100755 be/run-docker mode change 100644 => 100755 be/run-score diff --git a/be/build-api b/be/build-api old mode 100644 new mode 100755 diff --git a/be/build-compose b/be/build-compose new file mode 100755 index 0000000..ed1854d --- /dev/null +++ b/be/build-compose @@ -0,0 +1,21 @@ +sudo docker compose down +sudo docker image rm yechan/algo-with-me-api 2>/dev/null +sudo docker image rm yechan/algo-with-me-score 2>/dev/null +sudo docker image rm yechan/algo-with-me-docker 2>/dev/null + +cd algo-with-me-api/ +sudo docker build -t yechan/algo-with-me-api . +cd .. + +cd algo-with-me-score/ +sudo docker build -t yechan/algo-with-me-score . +cd .. + +cd algo-with-me-docker/ +sudo docker build -t yechan/algo-with-me-docker . +cd .. + +sudo docker login +sudo docker push yechan/algo-with-me-api +sudo docker push yechan/algo-with-me-score +sudo docker push yechan/algo-with-me-docker diff --git a/be/build-docker b/be/build-docker old mode 100644 new mode 100755 diff --git a/be/build-score b/be/build-score old mode 100644 new mode 100755 diff --git a/be/docker-compose.yaml b/be/docker-compose.yaml new file mode 100644 index 0000000..d3fc99c --- /dev/null +++ b/be/docker-compose.yaml @@ -0,0 +1,31 @@ +services: + algo-with-me-api: + image: yechan/algo-with-me-api + ports: + - 3000:3000 + volumes: + - /home/be/algo-with-me/problems/:/algo-with-me/problems/ + - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ + - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ + network_mode: host + restart: always + algo-with-me-score: + image: yechan/algo-with-me-score + ports: + - 4000:4000 + volumes: + - /home/be/algo-with-me/problems/:/algo-with-me/problems/ + - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ + - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ + network_mode: host + restart: always + algo-with-me-docker: + image: yechan/algo-with-me-docker + ports: + - 5000:5000 + volumes: + - /home/be/algo-with-me/problems/:/algo-with-me/problems/ + - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ + - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ + network_mode: host + user: 1001:1001 diff --git a/be/run-api b/be/run-api old mode 100644 new mode 100755 diff --git a/be/run-compose b/be/run-compose new file mode 100755 index 0000000..5662004 --- /dev/null +++ b/be/run-compose @@ -0,0 +1 @@ +sudo docker compose up \ No newline at end of file diff --git a/be/run-compose-background b/be/run-compose-background new file mode 100755 index 0000000..494f3c3 --- /dev/null +++ b/be/run-compose-background @@ -0,0 +1 @@ +sudo docker compose up -d \ No newline at end of file diff --git a/be/run-docker b/be/run-docker old mode 100644 new mode 100755 diff --git a/be/run-score b/be/run-score old mode 100644 new mode 100755 From 85b16688474716bc7011b395ad8ff73b9ae45622 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 29 Nov 2023 16:49:04 +0900 Subject: [PATCH 157/226] =?UTF-8?q?fix:=20.env=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=84=20docker=20registry=EC=97=90=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/.dockerignore | 1 + be/algo-with-me-score/.dockerignore | 1 + be/docker-compose.yaml | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/be/algo-with-me-api/.dockerignore b/be/algo-with-me-api/.dockerignore index 1eae0cf..a8b1485 100644 --- a/be/algo-with-me-api/.dockerignore +++ b/be/algo-with-me-api/.dockerignore @@ -1,2 +1,3 @@ dist/ node_modules/ +.env diff --git a/be/algo-with-me-score/.dockerignore b/be/algo-with-me-score/.dockerignore index 1eae0cf..5c84119 100644 --- a/be/algo-with-me-score/.dockerignore +++ b/be/algo-with-me-score/.dockerignore @@ -1,2 +1,3 @@ dist/ node_modules/ +.env \ No newline at end of file diff --git a/be/docker-compose.yaml b/be/docker-compose.yaml index d3fc99c..f9e1de0 100644 --- a/be/docker-compose.yaml +++ b/be/docker-compose.yaml @@ -8,6 +8,8 @@ services: - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ network_mode: host + env_file: + ./algo-with-me-api/.env restart: always algo-with-me-score: image: yechan/algo-with-me-score @@ -18,6 +20,8 @@ services: - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ network_mode: host + env_file: + - ./algo-with-me-score/.env restart: always algo-with-me-docker: image: yechan/algo-with-me-docker From 79b678ff563135a7da5dcd91fdd9d3534c393099 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 29 Nov 2023 16:59:15 +0900 Subject: [PATCH 158/226] =?UTF-8?q?chore:=20docker=20registry=EC=97=90=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EA=B3=A0=EB=8F=84=20=EC=8B=A4=ED=96=89=EC=9D=84=20=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/build-all | 19 +++++++++++++++++ be/build-api | 1 + be/build-compose | 21 ------------------- be/build-docker | 1 + be/build-score | 1 + be/docker-compose.yaml | 6 +++--- be/push-all | 4 ++++ be/{run-compose => run-all} | 0 ...-compose-background => run-all-background} | 0 9 files changed, 29 insertions(+), 24 deletions(-) create mode 100755 be/build-all delete mode 100755 be/build-compose create mode 100755 be/push-all rename be/{run-compose => run-all} (100%) rename be/{run-compose-background => run-all-background} (100%) diff --git a/be/build-all b/be/build-all new file mode 100755 index 0000000..0d55e6a --- /dev/null +++ b/be/build-all @@ -0,0 +1,19 @@ +sudo docker compose down +sudo docker container stop algo-with-me-api 2>/dev/null +sudo docker container stop algo-with-me-score 2>/dev/null +sudo docker container stop algo-with-me-docker 2>/dev/null +sudo docker image rm algo-with-me-api 2>/dev/null +sudo docker image rm algo-with-me-score 2>/dev/null +sudo docker image rm algo-with-me-docker 2>/dev/null + +cd algo-with-me-api/ +sudo docker build -t algo-with-me-api . +cd .. + +cd algo-with-me-score/ +sudo docker build -t algo-with-me-score . +cd .. + +cd algo-with-me-docker/ +sudo docker build -t algo-with-me-docker . +cd .. diff --git a/be/build-api b/be/build-api index dd32af5..29acdfc 100755 --- a/be/build-api +++ b/be/build-api @@ -1,3 +1,4 @@ +sudo docker compose down sudo docker container stop algo-with-me-api 2>/dev/null sudo docker container rm algo-with-me-api 2>/dev/null sudo docker image rm algo-with-me-api 2>/dev/null diff --git a/be/build-compose b/be/build-compose deleted file mode 100755 index ed1854d..0000000 --- a/be/build-compose +++ /dev/null @@ -1,21 +0,0 @@ -sudo docker compose down -sudo docker image rm yechan/algo-with-me-api 2>/dev/null -sudo docker image rm yechan/algo-with-me-score 2>/dev/null -sudo docker image rm yechan/algo-with-me-docker 2>/dev/null - -cd algo-with-me-api/ -sudo docker build -t yechan/algo-with-me-api . -cd .. - -cd algo-with-me-score/ -sudo docker build -t yechan/algo-with-me-score . -cd .. - -cd algo-with-me-docker/ -sudo docker build -t yechan/algo-with-me-docker . -cd .. - -sudo docker login -sudo docker push yechan/algo-with-me-api -sudo docker push yechan/algo-with-me-score -sudo docker push yechan/algo-with-me-docker diff --git a/be/build-docker b/be/build-docker index 1a1b194..282ec9e 100755 --- a/be/build-docker +++ b/be/build-docker @@ -1,3 +1,4 @@ +sudo docker compose down sudo docker container stop algo-with-me-docker 2>/dev/null sudo docker container rm algo-with-me-docker 2>/dev/null sudo docker image rm algo-with-me-docker 2>/dev/null diff --git a/be/build-score b/be/build-score index c7edcf3..6853f1c 100755 --- a/be/build-score +++ b/be/build-score @@ -1,3 +1,4 @@ +sudo docker compose down sudo docker container stop algo-with-me-score 2>/dev/null sudo docker container rm algo-with-me-score 2>/dev/null sudo docker image rm algo-with-me-score 2>/dev/null diff --git a/be/docker-compose.yaml b/be/docker-compose.yaml index f9e1de0..fafe120 100644 --- a/be/docker-compose.yaml +++ b/be/docker-compose.yaml @@ -1,6 +1,6 @@ services: algo-with-me-api: - image: yechan/algo-with-me-api + image: algo-with-me-api ports: - 3000:3000 volumes: @@ -12,7 +12,7 @@ services: ./algo-with-me-api/.env restart: always algo-with-me-score: - image: yechan/algo-with-me-score + image: algo-with-me-score ports: - 4000:4000 volumes: @@ -24,7 +24,7 @@ services: - ./algo-with-me-score/.env restart: always algo-with-me-docker: - image: yechan/algo-with-me-docker + image: algo-with-me-docker ports: - 5000:5000 volumes: diff --git a/be/push-all b/be/push-all new file mode 100755 index 0000000..483f1fd --- /dev/null +++ b/be/push-all @@ -0,0 +1,4 @@ +sudo docker login +sudo docker push yechan/algo-with-me-api +sudo docker push yechan/algo-with-me-score +sudo docker push yechan/algo-with-me-docker \ No newline at end of file diff --git a/be/run-compose b/be/run-all similarity index 100% rename from be/run-compose rename to be/run-all diff --git a/be/run-compose-background b/be/run-all-background similarity index 100% rename from be/run-compose-background rename to be/run-all-background From a2a5c0d04487ab705a8f04a5b18be44816255354 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 29 Nov 2023 17:09:34 +0900 Subject: [PATCH 159/226] =?UTF-8?q?chore:=20docker=20registry=EC=97=90=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EA=B3=A0=EB=8F=84=20=EC=8B=A4=ED=96=89=EC=9D=84=20=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/run-all | 6 ++++++ be/run-all-background | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/be/run-all b/be/run-all index 5662004..2651a2c 100755 --- a/be/run-all +++ b/be/run-all @@ -1 +1,7 @@ +sudo docker container stop algo-with-me-api 2>/dev/null +sudo docker container rm algo-with-me-api 2>/dev/null +sudo docker container stop algo-with-me-score 2>/dev/null +sudo docker container rm algo-with-me-score 2>/dev/null +sudo docker container stop algo-with-me-docker 2>/dev/null +sudo docker container rm algo-with-me-docker 2>/dev/null sudo docker compose up \ No newline at end of file diff --git a/be/run-all-background b/be/run-all-background index 494f3c3..31b0e4f 100755 --- a/be/run-all-background +++ b/be/run-all-background @@ -1 +1,7 @@ +sudo docker container stop algo-with-me-api 2>/dev/null +sudo docker container rm algo-with-me-api 2>/dev/null +sudo docker container stop algo-with-me-score 2>/dev/null +sudo docker container rm algo-with-me-score 2>/dev/null +sudo docker container stop algo-with-me-docker 2>/dev/null +sudo docker container rm algo-with-me-docker 2>/dev/null sudo docker compose up -d \ No newline at end of file From 4e2f0094f69e61093c5577e1df48571729a8d778 Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Wed, 29 Nov 2023 17:51:52 +0900 Subject: [PATCH 160/226] =?UTF-8?q?fix:=20run-api,=20run-score=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=8F=84=20.env=20=ED=8C=8C=EC=9D=BC=EC=97=90=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/run-api | 1 + be/run-score | 1 + 2 files changed, 2 insertions(+) diff --git a/be/run-api b/be/run-api index dfd8e53..5fbc51f 100755 --- a/be/run-api +++ b/be/run-api @@ -4,5 +4,6 @@ sudo docker run -d -p 3000:3000 \ -v /home/be/algo-with-me/problems/:/algo-with-me/problems/ \ -v /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ \ -v /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ \ +--env-file ./algo-with-me-api/.env \ --name algo-with-me-api \ algo-with-me-api diff --git a/be/run-score b/be/run-score index 5cb15ae..5e6e047 100755 --- a/be/run-score +++ b/be/run-score @@ -4,5 +4,6 @@ sudo docker run -d -p 4000:4000 \ -v /home/be/algo-with-me/problems/:/algo-with-me/problems/ \ -v /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ \ -v /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ \ +--env-file ./algo-with-me-score/.env \ --name algo-with-me-score \ algo-with-me-score \ No newline at end of file From 0aca6c7002ddf978ddb6636984c438786d49ab2a Mon Sep 17 00:00:00 2001 From: rladydgn <39542757+rladydgn@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:06:23 +0900 Subject: [PATCH 161/226] =?UTF-8?q?fix:=20=EA=B9=83=ED=97=88=EB=B8=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A6=AC=EB=8B=A4=EC=9D=B4?= =?UTF-8?q?=EB=A0=89=ED=8A=B8=20url=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/src/auth/controllers/auth.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts index 74865a9..bd404e1 100644 --- a/be/algo-with-me-api/src/auth/controllers/auth.controller.ts +++ b/be/algo-with-me-api/src/auth/controllers/auth.controller.ts @@ -29,7 +29,7 @@ export class AuthController { nickname: req.user.nickname, }; return { - url: `https://boostcampwm2023.github.io/web12-algo-with-me?accessToken=${this.jwtService.sign( + url: `https://www.algo-with-me.site?accessToken=${this.jwtService.sign( content, )}`, }; From 41e9402b64a73f321896076363655f37714d6e6f Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 30 Nov 2023 12:18:17 +0900 Subject: [PATCH 162/226] =?UTF-8?q?fix:=20be=20user=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=A8,=20be=20user?= =?UTF-8?q?=EB=A5=BC=20docker=20group=EC=97=90=20=EB=84=A3=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/build-all | 22 +++++++++++----------- be/build-api | 10 +++++----- be/build-docker | 10 +++++----- be/build-score | 10 +++++----- be/docker-compose.yaml | 4 +++- be/push-all | 8 ++++---- be/run-all | 14 +++++++------- be/run-all-background | 14 +++++++------- be/run-api | 9 +++++---- be/run-docker | 8 ++++---- be/run-score | 7 ++++--- 11 files changed, 60 insertions(+), 56 deletions(-) diff --git a/be/build-all b/be/build-all index 0d55e6a..f688662 100755 --- a/be/build-all +++ b/be/build-all @@ -1,19 +1,19 @@ -sudo docker compose down -sudo docker container stop algo-with-me-api 2>/dev/null -sudo docker container stop algo-with-me-score 2>/dev/null -sudo docker container stop algo-with-me-docker 2>/dev/null -sudo docker image rm algo-with-me-api 2>/dev/null -sudo docker image rm algo-with-me-score 2>/dev/null -sudo docker image rm algo-with-me-docker 2>/dev/null +docker compose down 2>/dev/null +docker container stop algo-with-me-api 2>/dev/null +docker container stop algo-with-me-score 2>/dev/null +docker container stop algo-with-me-docker 2>/dev/null +docker image rm algo-with-me-api 2>/dev/null +docker image rm algo-with-me-score 2>/dev/null +docker image rm algo-with-me-docker 2>/dev/null cd algo-with-me-api/ -sudo docker build -t algo-with-me-api . +docker build -t algo-with-me-api . cd .. cd algo-with-me-score/ -sudo docker build -t algo-with-me-score . +docker build -t algo-with-me-score . cd .. cd algo-with-me-docker/ -sudo docker build -t algo-with-me-docker . -cd .. +docker build -t algo-with-me-docker . +cd .. \ No newline at end of file diff --git a/be/build-api b/be/build-api index 29acdfc..ce70833 100755 --- a/be/build-api +++ b/be/build-api @@ -1,6 +1,6 @@ -sudo docker compose down -sudo docker container stop algo-with-me-api 2>/dev/null -sudo docker container rm algo-with-me-api 2>/dev/null -sudo docker image rm algo-with-me-api 2>/dev/null +docker compose down +docker container stop algo-with-me-api 2>/dev/null +docker container rm algo-with-me-api 2>/dev/null +docker image rm algo-with-me-api 2>/dev/null cd algo-with-me-api/ -sudo docker build -t algo-with-me-api . \ No newline at end of file +docker build -t algo-with-me-api . \ No newline at end of file diff --git a/be/build-docker b/be/build-docker index 282ec9e..6f0f105 100755 --- a/be/build-docker +++ b/be/build-docker @@ -1,6 +1,6 @@ -sudo docker compose down -sudo docker container stop algo-with-me-docker 2>/dev/null -sudo docker container rm algo-with-me-docker 2>/dev/null -sudo docker image rm algo-with-me-docker 2>/dev/null +docker compose down +docker container stop algo-with-me-docker 2>/dev/null +docker container rm algo-with-me-docker 2>/dev/null +docker image rm algo-with-me-docker 2>/dev/null cd algo-with-me-docker/ -sudo docker build -t algo-with-me-docker . \ No newline at end of file +docker build -t algo-with-me-docker . \ No newline at end of file diff --git a/be/build-score b/be/build-score index 6853f1c..fd80ca3 100755 --- a/be/build-score +++ b/be/build-score @@ -1,6 +1,6 @@ -sudo docker compose down -sudo docker container stop algo-with-me-score 2>/dev/null -sudo docker container rm algo-with-me-score 2>/dev/null -sudo docker image rm algo-with-me-score 2>/dev/null +docker compose down +docker container stop algo-with-me-score 2>/dev/null +docker container rm algo-with-me-score 2>/dev/null +docker image rm algo-with-me-score 2>/dev/null cd algo-with-me-score/ -sudo docker build -t algo-with-me-score . \ No newline at end of file +docker build -t algo-with-me-score . \ No newline at end of file diff --git a/be/docker-compose.yaml b/be/docker-compose.yaml index fafe120..3f7c6f0 100644 --- a/be/docker-compose.yaml +++ b/be/docker-compose.yaml @@ -10,6 +10,7 @@ services: network_mode: host env_file: ./algo-with-me-api/.env + user: 1001:1001 restart: always algo-with-me-score: image: algo-with-me-score @@ -22,6 +23,7 @@ services: network_mode: host env_file: - ./algo-with-me-score/.env + user: 1001:1001 restart: always algo-with-me-docker: image: algo-with-me-docker @@ -32,4 +34,4 @@ services: - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ network_mode: host - user: 1001:1001 + user: 1001:1001 \ No newline at end of file diff --git a/be/push-all b/be/push-all index 483f1fd..60a45a9 100755 --- a/be/push-all +++ b/be/push-all @@ -1,4 +1,4 @@ -sudo docker login -sudo docker push yechan/algo-with-me-api -sudo docker push yechan/algo-with-me-score -sudo docker push yechan/algo-with-me-docker \ No newline at end of file +docker login +docker push yechan/algo-with-me-api +docker push yechan/algo-with-me-score +docker push yechan/algo-with-me-docker \ No newline at end of file diff --git a/be/run-all b/be/run-all index 2651a2c..a5aedd4 100755 --- a/be/run-all +++ b/be/run-all @@ -1,7 +1,7 @@ -sudo docker container stop algo-with-me-api 2>/dev/null -sudo docker container rm algo-with-me-api 2>/dev/null -sudo docker container stop algo-with-me-score 2>/dev/null -sudo docker container rm algo-with-me-score 2>/dev/null -sudo docker container stop algo-with-me-docker 2>/dev/null -sudo docker container rm algo-with-me-docker 2>/dev/null -sudo docker compose up \ No newline at end of file +docker container stop algo-with-me-api 2>/dev/null +docker container rm algo-with-me-api 2>/dev/null +docker container stop algo-with-me-score 2>/dev/null +docker container rm algo-with-me-score 2>/dev/null +docker container stop algo-with-me-docker 2>/dev/null +docker container rm algo-with-me-docker 2>/dev/null +docker compose up \ No newline at end of file diff --git a/be/run-all-background b/be/run-all-background index 31b0e4f..dbcedec 100755 --- a/be/run-all-background +++ b/be/run-all-background @@ -1,7 +1,7 @@ -sudo docker container stop algo-with-me-api 2>/dev/null -sudo docker container rm algo-with-me-api 2>/dev/null -sudo docker container stop algo-with-me-score 2>/dev/null -sudo docker container rm algo-with-me-score 2>/dev/null -sudo docker container stop algo-with-me-docker 2>/dev/null -sudo docker container rm algo-with-me-docker 2>/dev/null -sudo docker compose up -d \ No newline at end of file +docker container stop algo-with-me-api 2>/dev/null +docker container rm algo-with-me-api 2>/dev/null +docker container stop algo-with-me-score 2>/dev/null +docker container rm algo-with-me-score 2>/dev/null +docker container stop algo-with-me-docker 2>/dev/null +docker container rm algo-with-me-docker 2>/dev/null +docker compose up -d \ No newline at end of file diff --git a/be/run-api b/be/run-api index 5fbc51f..18018c0 100755 --- a/be/run-api +++ b/be/run-api @@ -1,9 +1,10 @@ -sudo docker container stop algo-with-me-api 2>/dev/null -sudo docker container rm algo-with-me-api 2>/dev/null -sudo docker run -d -p 3000:3000 \ +docker container stop algo-with-me-api 2>/dev/null +docker container rm algo-with-me-api 2>/dev/null +docker run -d -p 3000:3000 \ -v /home/be/algo-with-me/problems/:/algo-with-me/problems/ \ -v /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ \ -v /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ \ --env-file ./algo-with-me-api/.env \ +--user 1001:1001 \ --name algo-with-me-api \ -algo-with-me-api +algo-with-me-api \ No newline at end of file diff --git a/be/run-docker b/be/run-docker index 65cef9c..6a1a611 100755 --- a/be/run-docker +++ b/be/run-docker @@ -1,6 +1,6 @@ -sudo docker container stop algo-with-me-docker 2>/dev/null -sudo docker container rm algo-with-me-docker 2>/dev/null -sudo docker run -d \ +docker container stop algo-with-me-docker 2>/dev/null +docker container rm algo-with-me-docker 2>/dev/null +docker run -d \ -p 5000:5000 \ -e COMPETITION_ID=$1 \ -e USER_ID=$2 \ @@ -9,6 +9,6 @@ sudo docker run -d \ -v /home/be/algo-with-me/problems:/algo-with-me/problems \ -v /home/be/algo-with-me/testcases:/algo-with-me/testcases \ -v /home/be/algo-with-me/submissions:/algo-with-me/submissions \ ---user "$(id -u)":"$(id -g)" \ +--user 1001:1001 \ --name algo-with-me-docker \ algo-with-me-docker \ No newline at end of file diff --git a/be/run-score b/be/run-score index 5e6e047..088e4c5 100755 --- a/be/run-score +++ b/be/run-score @@ -1,9 +1,10 @@ -sudo docker container stop algo-with-me-score 2>/dev/null -sudo docker container rm algo-with-me-score 2>/dev/null -sudo docker run -d -p 4000:4000 \ +docker container stop algo-with-me-score 2>/dev/null +docker container rm algo-with-me-score 2>/dev/null +docker run -d -p 4000:4000 \ -v /home/be/algo-with-me/problems/:/algo-with-me/problems/ \ -v /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ \ -v /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ \ --env-file ./algo-with-me-score/.env \ +--user 1001:1001 \ --name algo-with-me-score \ algo-with-me-score \ No newline at end of file From 294fbc8ea7feafba7f5feab4b7ea2d5a694a5ddd Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 30 Nov 2023 13:02:32 +0900 Subject: [PATCH 163/226] =?UTF-8?q?fix:=20=EB=AA=A8=EB=93=A0=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=20=EA=B6=8C=ED=95=9C=EC=9D=84=20be?= =?UTF-8?q?=EB=A1=9C=20=EC=98=AE=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/algo-with-me-api/Dockerfile | 11 ++++++++--- be/algo-with-me-docker/Dockerfile | 14 +++++++++----- be/algo-with-me-score/Dockerfile | 11 ++++++++--- be/docker-compose.yaml | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/be/algo-with-me-api/Dockerfile b/be/algo-with-me-api/Dockerfile index 5e2998d..d737c4e 100644 --- a/be/algo-with-me-api/Dockerfile +++ b/be/algo-with-me-api/Dockerfile @@ -1,10 +1,15 @@ FROM node:20 +RUN npm install -g pnpm + +RUN groupadd -g 1001 be \ + && useradd -r -m -u 1001 -g be be +USER be + WORKDIR /algo-with-me-api -COPY ./ ./ +COPY --chown=be:be ./ ./ -RUN npm install -g pnpm \ - && pnpm install +RUN pnpm install EXPOSE 3000 diff --git a/be/algo-with-me-docker/Dockerfile b/be/algo-with-me-docker/Dockerfile index 429767b..9f28128 100644 --- a/be/algo-with-me-docker/Dockerfile +++ b/be/algo-with-me-docker/Dockerfile @@ -4,14 +4,18 @@ ARG PORT=5000 FROM node:${NODE_VERSION}-bookworm AS base +RUN npm install -g pnpm + +RUN groupadd -g 1001 be \ + && useradd -r -m -u 1001 -g be be +USER be + CMD "mkdir -p /algo-with-me" WORKDIR /algo-with-me -COPY --chmod=555 ./node-sh /algo-with-me/node-sh -COPY . /algo-with-me +COPY --chmod=555 --chown=be:be ./node-sh /algo-with-me/node-sh +COPY --chown=be:be . /algo-with-me -RUN apt update \ - && npm install -g pnpm \ - && pnpm install +RUN pnpm install SHELL ["/bin/bash", "-ec"] diff --git a/be/algo-with-me-score/Dockerfile b/be/algo-with-me-score/Dockerfile index d6ea7c5..2dcc0dd 100644 --- a/be/algo-with-me-score/Dockerfile +++ b/be/algo-with-me-score/Dockerfile @@ -1,10 +1,15 @@ FROM node:20 +RUN npm install -g pnpm + +RUN groupadd -g 1001 be \ + && useradd -r -m -u 1001 -g be be +USER be + WORKDIR /algo-with-me-score -COPY ./ ./ +COPY --chown=be:be ./ ./ -RUN npm install -g pnpm \ - && pnpm install +RUN pnpm install EXPOSE 4000 diff --git a/be/docker-compose.yaml b/be/docker-compose.yaml index 3f7c6f0..6a2231f 100644 --- a/be/docker-compose.yaml +++ b/be/docker-compose.yaml @@ -34,4 +34,4 @@ services: - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ network_mode: host - user: 1001:1001 \ No newline at end of file + user: 1001:1001 From ded63bda4b3d18bfc483c4e62a058008c0ab029a Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 30 Nov 2023 13:07:04 +0900 Subject: [PATCH 164/226] =?UTF-8?q?fix:=20docker=20compose=20=EC=98=AE?= =?UTF-8?q?=EA=B2=A8=EC=84=9C=20=EC=8B=A4=ED=96=89=EB=90=98=EA=B2=8C=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- be/docker-compose.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/be/docker-compose.yaml b/be/docker-compose.yaml index 6a2231f..dc911ca 100644 --- a/be/docker-compose.yaml +++ b/be/docker-compose.yaml @@ -10,7 +10,7 @@ services: network_mode: host env_file: ./algo-with-me-api/.env - user: 1001:1001 + user: be:be restart: always algo-with-me-score: image: algo-with-me-score @@ -23,7 +23,7 @@ services: network_mode: host env_file: - ./algo-with-me-score/.env - user: 1001:1001 + user: be:be restart: always algo-with-me-docker: image: algo-with-me-docker @@ -34,4 +34,4 @@ services: - /home/be/algo-with-me/submissions/:/algo-with-me/submissions/ - /home/be/algo-with-me/testcases/:/algo-with-me/testcases/ network_mode: host - user: 1001:1001 + user: be:be From 1d7bf2ba4843b6797f35da117cd3f5794d4dcfda Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 30 Nov 2023 14:13:10 +0900 Subject: [PATCH 165/226] =?UTF-8?q?refactor:=20score.service.ts=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/fetch.service.ts | 55 ++++++++ .../src/score/services/filesystem.service.ts | 52 +++++++- .../src/score/services/score.consumer.ts | 1 - .../src/score/services/score.service.ts | 121 +++--------------- 4 files changed, 122 insertions(+), 107 deletions(-) create mode 100644 be/algo-with-me-score/src/score/services/fetch.service.ts diff --git a/be/algo-with-me-score/src/score/services/fetch.service.ts b/be/algo-with-me-score/src/score/services/fetch.service.ts new file mode 100644 index 0000000..e7524d3 --- /dev/null +++ b/be/algo-with-me-score/src/score/services/fetch.service.ts @@ -0,0 +1,55 @@ +import { InternalServerErrorException, Logger } from '@nestjs/common'; + +import { FilesystemService } from './filesystem.service'; +import { ScoreResultDto } from '../dtos/score-result.dto'; +import ICoderunResponse from '../interfaces/coderun-response.interface'; + +export class FetchService { + constructor(private readonly filesystemService: FilesystemService) {} + + async sendScoreResultToApiServer(scoreResult: ScoreResultDto) { + const [apiServerHost, apiServerPort] = [ + process.env.API_SERVER_HOST, + process.env.API_SERVER_PORT, + ]; + const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; + console.log(JSON.stringify(scoreResult)); + try { + const result = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(scoreResult), + }); + console.log(result.status); + } catch (error) { + new Logger().error( + `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, + ); + throw new InternalServerErrorException(); + } + } + + async requestDockerServerToRunCode( + competitionId: number, + userId: number, + problemId: number, + testcaseId: number, + ): Promise { + const [dockerServerHost, dockerServerPort] = [ + process.env.DOCKER_SERVER_HOST, + process.env.DOCKER_SERVER_PORT, + ]; + const url = `http://${dockerServerHost}:${dockerServerPort}/${competitionId}/${userId}/${problemId}/${testcaseId}`; + try { + const response = await fetch(url, { method: 'POST' }); + return (await response.json()) as ICoderunResponse; + } catch (error) { + new Logger().error( + `도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, + ); + throw new InternalServerErrorException(); + } + } +} diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index e16a91b..54d0ca9 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -1,4 +1,4 @@ -import { Logger } from '@nestjs/common'; +import { InternalServerErrorException, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; @@ -31,4 +31,54 @@ export class FilesystemService { private getMergedCode(code: string, frameCode: string) { return frameCode.replace('${0}', code); } + + getCodeRunOutputs( + competitionId: number, + userId: number, + problemId: number, + testcaseId: number, + ): { result: string; stdout: string; stderr: string; timeUsage: number; memoryUsage: number } { + const submissionPath = process.env.SUBMISSION_PATH; + const submissionBaseFilename = `${submissionPath}/${competitionId}/${userId}/${problemId}.${testcaseId}`; + const [resultFilepath, stdoutFilepath, stderrFilepath, timeUsageFilepath, memoryUsageFilepath] = + [ + `${submissionBaseFilename}.result`, + `${submissionBaseFilename}.stdout`, + `${submissionBaseFilename}.stderr`, + `${submissionBaseFilename}.time`, + `${submissionBaseFilename}.memory`, + ]; + if ( + !fs.existsSync(resultFilepath) || + !fs.existsSync(stdoutFilepath) || + !fs.existsSync(stderrFilepath) || + !fs.existsSync(timeUsageFilepath) || + !fs.existsSync(memoryUsageFilepath) + ) { + new Logger().error( + `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`, + ); + throw new InternalServerErrorException(); + } + + const [result, stdout, stderr, timeUsage, memoryUsage] = [ + fs.readFileSync(resultFilepath).toString(), + fs.readFileSync(stdoutFilepath).toString(), + fs.readFileSync(stderrFilepath).toString(), + parseInt(fs.readFileSync(timeUsageFilepath).toString()), + parseInt(fs.readFileSync(memoryUsageFilepath).toString()), + ]; + return { result, stdout, stderr, timeUsage, memoryUsage }; + } + + getTestcaseAnswer(problemId: number, testcaseId: number) { + const testcasePath = process.env.TESTCASE_PATH; + const filepath = `${testcasePath}/${problemId}/secrets/${testcaseId}.ans`; + if (!fs.existsSync(filepath)) { + new Logger().error(`경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`); + throw new InternalServerErrorException(); + } + + return fs.readFileSync(filepath).toString().trim(); + } } diff --git a/be/algo-with-me-score/src/score/services/score.consumer.ts b/be/algo-with-me-score/src/score/services/score.consumer.ts index 1cd372d..4175d6b 100644 --- a/be/algo-with-me-score/src/score/services/score.consumer.ts +++ b/be/algo-with-me-score/src/score/services/score.consumer.ts @@ -48,7 +48,6 @@ export class SubmissionConsumer { const problem: Problem = await this.problemRepository.findOneBy({ id: problemId }); await this.scoreService.scoreAllAndSendResult( - submission, problem.testcaseNum, submissionId, competitionId, diff --git a/be/algo-with-me-score/src/score/services/score.service.ts b/be/algo-with-me-score/src/score/services/score.service.ts index 1f66f18..5669d56 100644 --- a/be/algo-with-me-score/src/score/services/score.service.ts +++ b/be/algo-with-me-score/src/score/services/score.service.ts @@ -1,16 +1,18 @@ -import { InternalServerErrorException, Logger } from '@nestjs/common'; - -import * as fs from 'node:fs'; +import { Logger } from '@nestjs/common'; +import { FetchService } from './fetch.service'; +import { FilesystemService } from './filesystem.service'; import { ScoreResultDto } from '../dtos/score-result.dto'; import { Submission } from '../entities/submission.entity'; import ICoderunResponse from '../interfaces/coderun-response.interface'; export class ScoreService { - constructor() {} + constructor( + private readonly filesystemService: FilesystemService, + private readonly fetchService: FetchService, + ) {} public async scoreAllAndSendResult( - submission: Submission, testcaseNum: number, submissionId: number, competitionId: number, @@ -38,7 +40,12 @@ export class ScoreService { testcaseId: number, socketId: string, ) { - const codeRunResponse = await this.runCode(competitionId, userId, problemId, testcaseId); + const codeRunResponse = await this.fetchService.requestDockerServerToRunCode( + competitionId, + userId, + problemId, + testcaseId, + ); const { result: codeRunOutput, @@ -46,8 +53,8 @@ export class ScoreService { stderr, timeUsage, memoryUsage, - } = this.getCodeRunOutputs(competitionId, userId, problemId, testcaseId); - const testcaseAnswer = this.getTestcaseAnswer(problemId, testcaseId); + } = this.filesystemService.getCodeRunOutputs(competitionId, userId, problemId, testcaseId); + const testcaseAnswer = this.filesystemService.getTestcaseAnswer(problemId, testcaseId); const judgeResult = this.judge(codeRunResponse, codeRunOutput, testcaseAnswer); const scoreResult = new ScoreResultDto( @@ -60,7 +67,7 @@ export class ScoreService { timeUsage, memoryUsage, ); - await this.sendScoreResult(scoreResult); + await this.fetchService.sendScoreResultToApiServer(scoreResult); const logger = new Logger(); logger.debug( `채점 완료: ${JSON.stringify({ @@ -74,102 +81,6 @@ export class ScoreService { ); } - private async sendScoreResult(scoreResult: ScoreResultDto) { - const [apiServerHost, apiServerPort] = [ - process.env.API_SERVER_HOST, - process.env.API_SERVER_PORT, - ]; - const url = `http://${apiServerHost}:${apiServerPort}/competitions/scores`; - console.log(JSON.stringify(scoreResult)); - try { - const result = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(scoreResult), - }); - console.log(result.status); - } catch (error) { - new Logger().error( - `API 서버로 채점 결과를 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, - ); - throw new InternalServerErrorException(); - } - } - - private async runCode( - competitionId: number, - userId: number, - problemId: number, - testcaseId: number, - ): Promise { - const [dockerServerHost, dockerServerPort] = [ - process.env.DOCKER_SERVER_HOST, - process.env.DOCKER_SERVER_PORT, - ]; - const url = `http://${dockerServerHost}:${dockerServerPort}/${competitionId}/${userId}/${problemId}/${testcaseId}`; - try { - const response = await fetch(url, { method: 'POST' }); - return (await response.json()) as ICoderunResponse; - } catch (error) { - new Logger().error( - `도커 서버로 채점 요청을 보내는 데 실패했습니다 (POST ${url}) 원인: ${error}`, - ); - throw new InternalServerErrorException(); - } - } - - private getCodeRunOutputs( - competitionId: number, - userId: number, - problemId: number, - testcaseId: number, - ): { result: string; stdout: string; stderr: string; timeUsage: number; memoryUsage: number } { - const submissionPath = process.env.SUBMISSION_PATH; - const submissionBaseFilename = `${submissionPath}/${competitionId}/${userId}/${problemId}.${testcaseId}`; - const [resultFilepath, stdoutFilepath, stderrFilepath, timeUsageFilepath, memoryUsageFilepath] = - [ - `${submissionBaseFilename}.result`, - `${submissionBaseFilename}.stdout`, - `${submissionBaseFilename}.stderr`, - `${submissionBaseFilename}.time`, - `${submissionBaseFilename}.memory`, - ]; - if ( - !fs.existsSync(resultFilepath) || - !fs.existsSync(stdoutFilepath) || - !fs.existsSync(stderrFilepath) || - !fs.existsSync(timeUsageFilepath) || - !fs.existsSync(memoryUsageFilepath) - ) { - new Logger().error( - `${submissionBaseFilename}에 코드 실행 결과 파일들이 정상적으로 생성되지 않았습니다`, - ); - throw new InternalServerErrorException(); - } - - const [result, stdout, stderr, timeUsage, memoryUsage] = [ - fs.readFileSync(resultFilepath).toString(), - fs.readFileSync(stdoutFilepath).toString(), - fs.readFileSync(stderrFilepath).toString(), - parseInt(fs.readFileSync(timeUsageFilepath).toString()), - parseInt(fs.readFileSync(memoryUsageFilepath).toString()), - ]; - return { result, stdout, stderr, timeUsage, memoryUsage }; - } - - private getTestcaseAnswer(problemId: number, testcaseId: number) { - const testcasePath = process.env.TESTCASE_PATH; - const filepath = `${testcasePath}/${problemId}/secrets/${testcaseId}.ans`; - if (!fs.existsSync(filepath)) { - new Logger().error(`경로 ${filepath}에서 테스트케이스 ans 파일을 찾을 수 없습니다`); - throw new InternalServerErrorException(); - } - - return fs.readFileSync(filepath).toString().trim(); - } - private judge( codeRunResponse: ICoderunResponse, codeRunOutput: string, From 90d4a6d51890d85e5eea1d2a5d5f22127e4cdc8d Mon Sep 17 00:00:00 2001 From: Yechan Lee Date: Thu, 30 Nov 2023 14:41:01 +0900 Subject: [PATCH 166/226] =?UTF-8?q?refactor:=20filesystem.service.ts=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=EC=98=A4=EB=A5=98=20=EC=8B=9C=EC=97=90?= =?UTF-8?q?=20default=20=EA=B0=92=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/score/services/filesystem.service.ts | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/be/algo-with-me-score/src/score/services/filesystem.service.ts b/be/algo-with-me-score/src/score/services/filesystem.service.ts index 54d0ca9..5f8e1dd 100644 --- a/be/algo-with-me-score/src/score/services/filesystem.service.ts +++ b/be/algo-with-me-score/src/score/services/filesystem.service.ts @@ -9,18 +9,13 @@ import * as path from 'node:path'; import { Problem } from '../entities/problem.entity'; export class FilesystemService { - constructor( - @InjectRepository(Problem) private readonly problemRepository: Repository