diff --git a/README.md b/README.md index 00a13b1..bab9a34 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,3 @@ -

- Nest Logo -

- -[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.

-

-NPM Version -Package License -NPM Downloads -CircleCI -Coverage -Discord -Backers on Open Collective -Sponsors on Open Collective - - Support us - -

- - -## Description - -[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. - ## Installation ```bash @@ -57,17 +29,3 @@ $ npm run test:e2e # test coverage $ npm run test:cov ``` - -## Support - -Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). - -## Stay in touch - -- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) -- Website - [https://nestjs.com](https://nestjs.com/) -- Twitter - [@nestframework](https://twitter.com/nestframework) - -## License - -Nest is [MIT licensed](LICENSE). diff --git a/package-lock.json b/package-lock.json index a07a44b..851f62c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@nestjs-modules/mailer": "^2.0.2", + "@nestjs-modules/mailer": "^1.6.1", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", @@ -77,9 +77,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", + "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -103,13 +103,22 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@angular-devkit/schematics": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.8.tgz", - "integrity": "sha512-QRVEYpIfgkprNHc916JlPuNbLzOgrm9DZalHasnLUz4P6g7pR21olb8YCyM2OTJjombNhya9ZpckcADU5Qyvlg==", + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", + "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", + "@angular-devkit/core": "17.3.11", "jsonc-parser": "3.2.1", "magic-string": "0.30.8", "ora": "5.4.1", @@ -143,51 +152,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", - "dev": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/schematics": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", - "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "17.3.11", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@angular-devkit/schematics-cli/node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -253,44 +217,54 @@ "node": ">=0.12.0" } }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -315,29 +289,30 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.6", + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -355,28 +330,27 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -386,160 +360,58 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "devOptional": true, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "devOptional": true, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", - "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "devOptional": true, + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -600,12 +472,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", - "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -639,12 +511,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -756,12 +628,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", - "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -771,42 +643,38 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", - "optional": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -824,14 +692,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "devOptional": true, + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -876,59 +742,27 @@ } }, "node_modules/@css-inline/css-inline": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.14.1.tgz", - "integrity": "sha512-u4eku+hnPqqHIGq/ZUQcaP0TrCbYeLIYBaK7qClNRGZbnh8RC4gVxLEIo8Pceo1nOK9E5G4Lxzlw5KnXcvflfA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.13.0.tgz", + "integrity": "sha512-ZozAXBiW1I8hf6eW5eTNqhxUdNOBxrNNxxUnQRiKQpWcs5ORuGaiWwV5focMBTJ5WXGN+Z8VLP93BOwWFPzCJw==", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@css-inline/css-inline-android-arm-eabi": "0.14.1", - "@css-inline/css-inline-android-arm64": "0.14.1", - "@css-inline/css-inline-darwin-arm64": "0.14.1", - "@css-inline/css-inline-darwin-x64": "0.14.1", - "@css-inline/css-inline-linux-arm-gnueabihf": "0.14.1", - "@css-inline/css-inline-linux-arm64-gnu": "0.14.1", - "@css-inline/css-inline-linux-arm64-musl": "0.14.1", - "@css-inline/css-inline-linux-x64-gnu": "0.14.1", - "@css-inline/css-inline-linux-x64-musl": "0.14.1", - "@css-inline/css-inline-win32-x64-msvc": "0.14.1" - } - }, - "node_modules/@css-inline/css-inline-android-arm-eabi": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm-eabi/-/css-inline-android-arm-eabi-0.14.1.tgz", - "integrity": "sha512-LNUR8TY4ldfYi0mi/d4UNuHJ+3o8yLQH9r2Nt6i4qeg1i7xswfL3n/LDLRXvGjBYqeEYNlhlBQzbPwMX1qrU6A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-android-arm64": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm64/-/css-inline-android-arm64-0.14.1.tgz", - "integrity": "sha512-tH5us0NYGoTNBHOUHVV7j9KfJ4DtFOeTLA3cM0XNoMtArNu2pmaaBMFJPqECzavfXkLc7x5Z22UPZYjoyHfvCA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" + "@css-inline/css-inline-darwin-arm64": "0.13.0", + "@css-inline/css-inline-darwin-x64": "0.13.0", + "@css-inline/css-inline-linux-arm-gnueabihf": "0.13.0", + "@css-inline/css-inline-linux-arm64-gnu": "0.13.0", + "@css-inline/css-inline-linux-arm64-musl": "0.13.0", + "@css-inline/css-inline-linux-x64-gnu": "0.13.0", + "@css-inline/css-inline-linux-x64-musl": "0.13.0", + "@css-inline/css-inline-win32-x64-msvc": "0.13.0" } }, "node_modules/@css-inline/css-inline-darwin-arm64": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.14.1.tgz", - "integrity": "sha512-QE5W1YRIfRayFrtrcK/wqEaxNaqLULPI0gZB4ArbFRd3d56IycvgBasDTHPre5qL2cXCO3VyPx+80XyHOaVkag==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.13.0.tgz", + "integrity": "sha512-A4QvlZdhp8v+3IHKF/UftRf5GrAVUMEHCGRuk2Dx594xn/UR4ieh+B70aMm5rfONh2hv5mlR9UcoYAkVpEQ99g==", "cpu": [ "arm64" ], @@ -941,9 +775,9 @@ } }, "node_modules/@css-inline/css-inline-darwin-x64": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.14.1.tgz", - "integrity": "sha512-mAvv2sN8awNFsbvBzlFkZPbCNZ6GCWY5/YcIz7V5dPYw+bHHRbjnlkNTEZq5BsDxErVrMIGvz05PGgzuNvZvdQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.13.0.tgz", + "integrity": "sha512-px9z4ypzeECMyBEtlrNzTMpA1tnw5MmMIiMkBRhb8UGRr2pOBZY3yd/eEIxWzVVSPt0aIjVDwUOJ3+d0Z+BskA==", "cpu": [ "x64" ], @@ -956,9 +790,9 @@ } }, "node_modules/@css-inline/css-inline-linux-arm-gnueabihf": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.14.1.tgz", - "integrity": "sha512-AWC44xL0X7BgKvrWEqfSqkT2tJA5kwSGrAGT+m0gt11wnTYySvQ6YpX0fTY9i3ppYGu4bEdXFjyK2uY1DTQMHA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.13.0.tgz", + "integrity": "sha512-+uo0coLQNgk/AKeOB8mXSRd8VIlUg38zRSB9B9q0ior9oBCDPtEdn1HuCSvWxHoOSJ8QNNk+uwbz0zW4CETzFw==", "cpu": [ "arm" ], @@ -971,9 +805,9 @@ } }, "node_modules/@css-inline/css-inline-linux-arm64-gnu": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.14.1.tgz", - "integrity": "sha512-drj0ciiJgdP3xKXvNAt4W+FH4KKMs8vB5iKLJ3HcH07sNZj58Sx++2GxFRS1el3p+GFp9OoYA6dgouJsGEqt0Q==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.13.0.tgz", + "integrity": "sha512-GVrsFbY5l0Hxyzxsm5S5JPGObvHm/Ybf2wZgnWBsQigxqGtr1FL535HaTwEnq6aHOpH3f08gR5Vx33gB7jG4pw==", "cpu": [ "arm64" ], @@ -986,9 +820,9 @@ } }, "node_modules/@css-inline/css-inline-linux-arm64-musl": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.14.1.tgz", - "integrity": "sha512-FzknI+st8eA8YQSdEJU9ykcM0LZjjigBuynVF5/p7hiMm9OMP8aNhWbhZ8LKJpKbZrQsxSGS4g9Vnr6n6FiSdQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.13.0.tgz", + "integrity": "sha512-V5h5+CRnE01EgoafI/kyjEcM8zvN+sKLnp17Aq9LqQfsut7mO3i72d8g/xeVC37DCLoGQFLvDCzbze2NbF2dIQ==", "cpu": [ "arm64" ], @@ -1001,9 +835,9 @@ } }, "node_modules/@css-inline/css-inline-linux-x64-gnu": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.14.1.tgz", - "integrity": "sha512-yubbEye+daDY/4vXnyASAxH88s256pPati1DfVoZpU1V0+KP0BZ1dByZOU1ktExurbPH3gZOWisAnBE9xon0Uw==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.13.0.tgz", + "integrity": "sha512-vbRV++73MW7dvz/AIbozkv4R68/k/sEp57hno/L6lx034VYxpCwdfqtGN4D0W1TOTzdr2b6qBOGNZ1oLKQZOQQ==", "cpu": [ "x64" ], @@ -1016,9 +850,9 @@ } }, "node_modules/@css-inline/css-inline-linux-x64-musl": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.14.1.tgz", - "integrity": "sha512-6CRAZzoy1dMLPC/tns2rTt1ZwPo0nL/jYBEIAsYTCWhfAnNnpoLKVh5Nm+fSU3OOwTTqU87UkGrFJhObD/wobQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.13.0.tgz", + "integrity": "sha512-2tCnwU23W/yMs9cGc2/i2jd9y2pjuntx0a5OytqX7s9fvUtmI3nc0Od6wuf51LnmdU+XAU8HLT9pZppsQiwPfQ==", "cpu": [ "x64" ], @@ -1031,9 +865,9 @@ } }, "node_modules/@css-inline/css-inline-win32-x64-msvc": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.14.1.tgz", - "integrity": "sha512-nzotGiaiuiQW78EzsiwsHZXbxEt6DiMUFcDJ6dhiliomXxnlaPyBfZb6/FMBgRJOf6sknDt/5695OttNmbMYzg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.13.0.tgz", + "integrity": "sha512-6VFhFSXp4FH+NzJhLd6fFi7jKCPvIRW+vq0tV+CPuiQ3zPzMfC9nIk8sB/1VJR8EcvBAjMV53YnacuDjRFRT9g==", "cpu": [ "x64" ], @@ -1046,24 +880,27 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1721,9 +1558,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -1799,90 +1636,46 @@ } }, "node_modules/@microsoft/tsdoc": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", - "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==" + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz", - "integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", + "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", "dependencies": { "sparse-bitfield": "^3.0.3" } }, "node_modules/@nestjs-modules/mailer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-2.0.2.tgz", - "integrity": "sha512-+z4mADQasg0H1ZaGu4zZTuKv2pu+XdErqx99PLFPzCDNTN/q9U59WPgkxVaHnsvKHNopLj5Xap7G4ZpptduoYw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-1.11.2.tgz", + "integrity": "sha512-k07wyKbtCzxWMm6IqGwcGIisnXD/6sneGvUR8rBBZbxtLn1HE1FLGyiaXBrPui/0K7W41aS9x9jAIhfTawtlUg==", "dependencies": { - "@css-inline/css-inline": "0.14.1", - "glob": "10.3.12" + "@css-inline/css-inline": "0.13.0", + "glob": "10.3.10", + "mjml": "4.15.3", + "preview-email": "3.0.19" }, "optionalDependencies": { "@types/ejs": "^3.1.5", - "@types/mjml": "^4.7.4", "@types/pug": "^2.0.10", - "ejs": "^3.1.10", + "ejs": "^3.1.9", "handlebars": "^4.7.8", - "liquidjs": "^10.11.1", - "mjml": "^4.15.3", - "preview-email": "^3.0.19", "pug": "^3.0.2" }, "peerDependencies": { "@nestjs/common": ">=7.0.9", "@nestjs/core": ">=7.0.9", "@types/ejs": ">=3.0.3", - "@types/mjml": ">=4.7.4", "@types/pug": ">=2.0.6", "ejs": ">=3.1.2", "handlebars": ">=4.7.6", - "liquidjs": ">=10.8.2", - "mjml": ">=4.15.3", "nodemailer": ">=6.4.6", - "preview-email": ">=3.0.19", "pug": ">=3.0.1" } }, - "node_modules/@nestjs-modules/mailer/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@nestjs-modules/mailer/node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/@nestjs/cli": { "version": "10.4.9", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz", @@ -1928,58 +1721,130 @@ } } }, - "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", + "node_modules/@nestjs/cli/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=8.0.0" + } + }, + "node_modules/@nestjs/cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nestjs/cli/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "peerDependencies": { - "chokidar": "^3.5.2" + "bin": { + "glob": "dist/esm/bin.mjs" }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nestjs/cli/node_modules/@angular-devkit/schematics": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", - "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", + "node_modules/@nestjs/cli/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.11", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", - "ora": "5.4.1", - "rxjs": "7.8.1" + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@nestjs/cli/node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=14.17" + } + }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, "node_modules/@nestjs/common": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.3.tgz", - "integrity": "sha512-4hbLd3XIJubHSylYd/1WSi4VQvG68KM/ECYpMDqA3k3J1/T17SAg40sDoq3ZoO5OZgU0xuNyjuISdOTjs11qVg==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.19.tgz", + "integrity": "sha512-0TZJ8H+7qtaqZt6YfZJkDRp0e+v6jjo5/pevPAjUy0WYxaTy16bNNQxFPRKLMe/v1hUr2oGV9imvL2477zNt5g==", "dependencies": { + "file-type": "20.4.1", "iterare": "1.2.1", - "tslib": "2.7.0", + "tslib": "2.8.1", "uid": "2.0.2" }, "funding": { @@ -2002,9 +1867,9 @@ } }, "node_modules/@nestjs/config": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.3.tgz", - "integrity": "sha512-p6yv/CvoBewJ72mBq4NXgOAi2rSQNWx3a+IMJLVKS2uiwFCOQQuiIatGwq6MRjXV3Jr+B41iUO8FIf4xBrZ4/w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", + "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", "dependencies": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", @@ -2016,16 +1881,16 @@ } }, "node_modules/@nestjs/core": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.3.tgz", - "integrity": "sha512-6OQz+5C8mT8yRtfvE5pPCq+p6w5jDot+oQku1KzQ24ABn+lay1KGuJwcKZhdVNuselx+8xhdMxknZTA8wrGLIg==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.19.tgz", + "integrity": "sha512-gahghu0y4Rn4gn/xPjTgNHFMpUM8TxfhdeMowVWTGVnYMZtGeEGbIXMFhJS0Dce3E4VKyqAglzgO9ecAZd4Ong==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", "path-to-regexp": "3.3.0", - "tslib": "2.7.0", + "tslib": "2.8.1", "uid": "2.0.2" }, "funding": { @@ -2053,9 +1918,9 @@ } }, "node_modules/@nestjs/event-emitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.0.tgz", - "integrity": "sha512-WbvzQQ9BGnj27onh2qSLND2+4iA6Pfp4K+HLlqunB0Uz0614O8lGMtcveSss2IOxsox8EhSI54WAvuAsDrX1hA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", + "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", "dependencies": { "eventemitter2": "6.4.9" }, @@ -2077,11 +1942,11 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", - "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", + "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/common": "^10.0.0 || ^11.0.0", "class-transformer": "^0.4.0 || ^0.5.0", "class-validator": "^0.13.0 || ^0.14.0", "reflect-metadata": "^0.1.12 || ^0.2.0" @@ -2096,9 +1961,9 @@ } }, "node_modules/@nestjs/mongoose": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-11.0.1.tgz", - "integrity": "sha512-Nsw/eW5ZptIgwv3gPumKe6UtZu/0HZqixwkkO8Hqn6wZyrLeaTpBFi/z3DGY36dp66uPw14huradDY7OTPDYJA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-11.0.3.tgz", + "integrity": "sha512-tg7bbKD4MnNMPaiDLXK/JUyTNQxIn3rNnI+oYU1HorLpNiR2E8vPraWVvfptpIj+zferpT6LkrHMvtqvuIKNPw==", "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "@nestjs/core": "^10.0.0 || ^11.0.0", @@ -2116,15 +1981,15 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.3.tgz", - "integrity": "sha512-ss7gkofVm3eO+1P9iRhmGq6Xcjg+mIN3dWisKJZYelSV+msb0QpJmqChLvWjLkWtlqDnx915FKUk0IzCa0TVzw==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.19.tgz", + "integrity": "sha512-IeQkBZUtPeJoO4E0QqSLwkB+60KcThw8/s4gGvAwIRJ5ViuXoxnwU59eBDy84PUuVbNe4VdKjfAF9fuQOEh11Q==", "dependencies": { "body-parser": "1.20.3", "cors": "2.8.5", - "express": "4.21.0", - "multer": "1.4.4-lts.1", - "tslib": "2.7.0" + "express": "4.21.2", + "multer": "2.0.1", + "tslib": "2.8.1" }, "funding": { "type": "opencollective", @@ -2136,12 +2001,12 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.5.tgz", - "integrity": "sha512-dHkHJQArhrpkX6qBdTW2ghuja3i3cCslwy4QHY6d46u+9UyANQlsNK9wt/lZnmXfCMaci8xAJvUpyODa6YtV7g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.19.tgz", + "integrity": "sha512-Pfz9dBcXaoUFdVpA/I96NoOTiTQ7eYfDNhQ2sZdQzne3oISEvts5G2SWyQNouoyjqkUPt6X5r/CQ++M4AzSlEQ==", "dependencies": { - "socket.io": "4.7.5", - "tslib": "2.7.0" + "socket.io": "4.8.1", + "tslib": "2.8.1" }, "funding": { "type": "opencollective", @@ -2154,14 +2019,14 @@ } }, "node_modules/@nestjs/schematics": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", - "integrity": "sha512-QpY8ez9cTvXXPr3/KBrtSgXQHMSV6BkOUYy2c2TTe6cBqriEdGnCYqGl8cnfrQl3632q3lveQPaZ/c127dHsEw==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", + "integrity": "sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "comment-json": "4.2.3", + "@angular-devkit/core": "17.3.11", + "@angular-devkit/schematics": "17.3.11", + "comment-json": "4.2.5", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, @@ -2207,13 +2072,32 @@ } } }, + "node_modules/@nestjs/swagger/node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.3.tgz", - "integrity": "sha512-SBNWrMU51YAlYmW86wyjlGZ2uLnASNiOPD0lBcNIlxxei0b05/aI3nh7OPuxbXQUdedUJfPq2d2jZj4TRG4S0w==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.19.tgz", + "integrity": "sha512-YfzkjTmwEcoWqo8xr8YiTZMC4FjBEOg4uRTAPI2p6iGLWu+27tYau1CtAKFHY0uSAK3FzgtsAuYoxBSlfr9mWA==", "dev": true, "dependencies": { - "tslib": "2.7.0" + "tslib": "2.8.1" }, "funding": { "type": "opencollective", @@ -2235,13 +2119,13 @@ } }, "node_modules/@nestjs/websockets": { - "version": "10.4.7", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.7.tgz", - "integrity": "sha512-ajuoptYLYm+l3+KtaA9Ed+cO9yB34PtBE8UObavRT8Euh/f7QfeJiKcrU3+BQSAiTWM3nF2qfuV4CfEkP9uKuw==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.19.tgz", + "integrity": "sha512-3HhNZU40/ozB64ZZJ1W1bOOYSbxGuJwmM5Ui8y1uPwbzpL1Uov3wOG3eRp1IflSBK4Ia0wb8Iv3ChpFSTzrNiA==", "dependencies": { "iterare": "1.2.1", "object-hash": "3.0.0", - "tslib": "2.7.0" + "tslib": "2.8.1" }, "peerDependencies": { "@nestjs/common": "^10.0.0", @@ -2256,6 +2140,18 @@ } } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2311,8 +2207,16 @@ "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "optional": true + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "dependencies": { + "@noble/hashes": "^1.1.5" + } }, "node_modules/@phc/format": { "version": "1.0.0", @@ -2332,21 +2236,21 @@ } }, "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", "dev": true, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/pkgr" } }, "node_modules/@prisma/client": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.20.0.tgz", - "integrity": "sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", "hasInstallScript": true, "engines": { "node": ">=16.13" @@ -2361,50 +2265,49 @@ } }, "node_modules/@prisma/debug": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.20.0.tgz", - "integrity": "sha512-oCx79MJ4HSujokA8S1g0xgZUGybD4SyIOydoHMngFYiwEwYDQ5tBQkK5XoEHuwOYDKUOKRn/J0MEymckc4IgsQ==" + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==" }, "node_modules/@prisma/engines": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.20.0.tgz", - "integrity": "sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", "hasInstallScript": true, "dependencies": { - "@prisma/debug": "5.20.0", - "@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "@prisma/fetch-engine": "5.20.0", - "@prisma/get-platform": "5.20.0" + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" } }, "node_modules/@prisma/engines-version": { - "version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284.tgz", - "integrity": "sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA==" + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==" }, "node_modules/@prisma/fetch-engine": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.20.0.tgz", - "integrity": "sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", "dependencies": { - "@prisma/debug": "5.20.0", - "@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "@prisma/get-platform": "5.20.0" + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" } }, "node_modules/@prisma/get-platform": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.20.0.tgz", - "integrity": "sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", "dependencies": { - "@prisma/debug": "5.20.0" + "@prisma/debug": "5.22.0" } }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", - "optional": true, "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" @@ -2442,6 +2345,28 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -2480,9 +2405,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" @@ -2499,18 +2424,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" } }, "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==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "dependencies": { "@types/connect": "*", @@ -2526,17 +2451,12 @@ "@types/node": "*" } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, "node_modules/@types/cookie-parser": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", - "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", "dev": true, - "dependencies": { + "peerDependencies": { "@types/express": "*" } }, @@ -2547,9 +2467,9 @@ "dev": true }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dependencies": { "@types/node": "*" } @@ -2581,15 +2501,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "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==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -2599,9 +2519,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "dependencies": { "@types/node": "*", @@ -2620,9 +2540,9 @@ } }, "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==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true }, "node_modules/@types/istanbul-lib-coverage": { @@ -2650,9 +2570,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.13", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", - "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -2685,33 +2605,18 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, - "node_modules/@types/mjml": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", - "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==", - "optional": true, - "dependencies": { - "@types/mjml-core": "*" - } - }, - "node_modules/@types/mjml-core": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.0.tgz", - "integrity": "sha512-jSRWTOpwRS/uHIBfGdvLl0a7MaoBZZYHKI+HhsFYChrUOKVJTnjSYsuV6wx0snv6ZaX3TUo5OP/gNsz/uzZz1A==", - "optional": true - }, "node_modules/@types/node": { - "version": "20.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", - "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", + "version": "20.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", + "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/passport": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", - "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", "dev": true, "dependencies": { "@types/express": "*" @@ -2744,9 +2649,9 @@ "optional": true }, "node_modules/@types/qs": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", - "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true }, "node_modules/@types/range-parser": { @@ -2756,9 +2661,9 @@ "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==", + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "dev": true, "dependencies": { "@types/mime": "^1", @@ -2766,9 +2671,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "dev": true, "dependencies": { "@types/http-errors": "*", @@ -2795,9 +2700,9 @@ } }, "node_modules/@types/supertest": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", - "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", + "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", "dev": true, "dependencies": { "@types/methods": "^1.1.4", @@ -2805,9 +2710,9 @@ } }, "node_modules/@types/validator": { - "version": "13.12.2", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", - "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==" + "version": "13.15.1", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.1.tgz", + "integrity": "sha512-9gG6ogYcoI2mCMLdcO0NYI0AYrbxIjv0MDmy/5Ywo6CpWWrqYayc+mmgxRsCgtcGJm9BSbXkMsmxGah1iGHAAQ==" }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", @@ -3023,9 +2928,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, "node_modules/@webassemblyjs/ast": { @@ -3190,7 +3095,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "optional": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -3208,9 +3112,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3273,11 +3177,22 @@ } } }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "node_modules/alce": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/alce/-/alce-1.2.0.tgz", "integrity": "sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==", - "optional": true, "dependencies": { "esprima": "^1.2.0", "estraverse": "^1.5.0" @@ -3290,7 +3205,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", "integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==", - "optional": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3303,7 +3217,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -3312,7 +3225,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "devOptional": true, "engines": { "node": ">=6" } @@ -3370,7 +3282,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3383,7 +3294,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, "engines": { "node": ">=8.6" }, @@ -3444,14 +3354,12 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "devOptional": true + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/assert-never": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.3.0.tgz", - "integrity": "sha512-9Z3vxQ+berkL/JJo0dK+EY3Lp0s3NtSnP3VCLsh5HDcZPrh0M+KQRK5sWhUeyPPH+/RCxZqOxLMR+YC6vlviEQ==", - "optional": true + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==" }, "node_modules/async": { "version": "3.2.6", @@ -3596,7 +3504,6 @@ "version": "3.0.0-canary-5", "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", - "optional": true, "dependencies": { "@babel/types": "^7.9.6" }, @@ -3641,7 +3548,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "devOptional": true, "engines": { "node": ">=8" }, @@ -3655,23 +3561,9 @@ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, "node_modules/body-parser": { @@ -3713,8 +3605,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "optional": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -3728,7 +3619,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "devOptional": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -3737,9 +3627,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -3756,10 +3646,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -3790,9 +3680,9 @@ } }, "node_modules/bson": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.2.tgz", - "integrity": "sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "engines": { "node": ">=16.20.1" } @@ -3854,6 +3744,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -3879,6 +3770,21 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3892,7 +3798,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", - "optional": true, "dependencies": { "no-case": "^2.2.0", "upper-case": "^1.1.1" @@ -3908,9 +3813,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001721", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", + "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", "dev": true, "funding": [ { @@ -3955,7 +3860,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", - "optional": true, "dependencies": { "is-regex": "^1.0.3" } @@ -3970,7 +3874,6 @@ "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "optional": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -3991,7 +3894,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "optional": true, "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -4008,7 +3910,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "devOptional": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4041,7 +3942,6 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "devOptional": true, "funding": [ { "type": "github", @@ -4053,9 +3953,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, "node_modules/class-transformer": { @@ -4064,12 +3964,12 @@ "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" }, "node_modules/class-validator": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", - "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", + "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", "dependencies": { "@types/validator": "^13.11.8", - "libphonenumber-js": "^1.10.53", + "libphonenumber-js": "^1.11.1", "validator": "^13.9.0" } }, @@ -4077,7 +3977,6 @@ "version": "4.2.4", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", - "optional": true, "dependencies": { "source-map": "~0.6.0" }, @@ -4089,7 +3988,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -4146,7 +4044,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "devOptional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -4160,7 +4057,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "devOptional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4236,9 +4132,9 @@ } }, "node_modules/comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", "dev": true, "dependencies": { "array-timsort": "^1.0.3", @@ -4267,16 +4163,16 @@ "devOptional": true }, "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "engines": [ - "node >= 0.8" + "node >= 6.0" ], "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, @@ -4284,7 +4180,6 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "optional": true, "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -4299,7 +4194,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "optional": true, "dependencies": { "@babel/parser": "^7.6.0", "@babel/types": "^7.6.1" @@ -4334,17 +4228,16 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "dependencies": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "engines": { @@ -4352,9 +4245,9 @@ } }, "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -4373,7 +4266,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cors": { "version": "2.8.5", @@ -4441,9 +4335,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4457,7 +4351,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "optional": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -4473,7 +4366,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "optional": true, "engines": { "node": ">= 6" }, @@ -4482,9 +4374,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dependencies": { "ms": "^2.1.3" }, @@ -4498,9 +4390,9 @@ } }, "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", "dev": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -4515,7 +4407,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true, "engines": { "node": ">=4.0.0" } @@ -4530,7 +4421,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -4551,6 +4441,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -4593,7 +4484,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "optional": true, "engines": { "node": ">=8" } @@ -4602,7 +4492,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "devOptional": true, "engines": { "node": ">=8" } @@ -4610,8 +4499,7 @@ "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "optional": true + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, "node_modules/dezalgo": { "version": "1.0.4", @@ -4657,7 +4545,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/display-notification/-/display-notification-2.0.0.tgz", "integrity": "sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==", - "optional": true, "dependencies": { "escape-string-applescript": "^1.0.0", "run-applescript": "^3.0.0" @@ -4681,14 +4568,12 @@ "node_modules/doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", - "optional": true + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==" }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "optional": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -4707,14 +4592,12 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ], - "optional": true + ] }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "optional": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -4726,10 +4609,9 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "optional": true, + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -4758,6 +4640,19 @@ "node": ">=12" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4775,7 +4670,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", - "optional": true, "dependencies": { "@one-ini/wasm": "0.1.1", "commander": "^10.0.0", @@ -4793,7 +4687,6 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "optional": true, "engines": { "node": ">=14" } @@ -4802,7 +4695,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "optional": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4834,9 +4726,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.102", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", - "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", + "version": "1.5.165", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz", + "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==", "dev": true }, "node_modules/emittery": { @@ -4865,25 +4757,23 @@ } }, "node_modules/encoding-japanese": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.1.0.tgz", - "integrity": "sha512-58XySVxUgVlBikBTbQ8WdDxBDHIdXucB16LO5PBHR8t75D54wQrNo4cg+58+R1CtJfKnsVsvt9XlteRaR8xw1w==", - "optional": true, + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", "engines": { "node": ">=8.10.0" } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", "dependencies": { - "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -4902,17 +4792,33 @@ } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -4926,7 +4832,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "optional": true, "engines": { "node": ">=0.12" }, @@ -4944,12 +4849,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -4963,16 +4865,41 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "devOptional": true, "engines": { "node": ">=6" } @@ -4981,7 +4908,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", - "optional": true, "engines": { "node": ">=10" }, @@ -4998,7 +4924,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz", "integrity": "sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -5019,6 +4944,7 @@ "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -5083,13 +5009,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz", + "integrity": "sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -5100,7 +5026,7 @@ "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", - "eslint-config-prettier": "*", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -5345,16 +5271,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -5368,7 +5294,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -5383,13 +5309,16 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "license": "MIT", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -5408,15 +5337,14 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/extend-object": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", - "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==", - "optional": true + "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==" }, "node_modules/external-editor": { "version": "3.1.0", @@ -5445,16 +5373,16 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -5478,9 +5406,9 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -5495,6 +5423,11 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -5531,6 +5464,23 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "20.4.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", + "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -5556,7 +5506,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "devOptional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5614,7 +5563,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/fixpack/-/fixpack-4.0.0.tgz", "integrity": "sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==", - "optional": true, "dependencies": { "alce": "1.2.0", "chalk": "^3.0.0", @@ -5631,7 +5579,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "optional": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5655,17 +5602,17 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -5726,13 +5673,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5740,15 +5689,18 @@ } }, "node_modules/formidable": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", - "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -5829,21 +5781,25 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "devOptional": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5865,7 +5821,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "optional": true, "engines": { "node": ">=8" }, @@ -5873,6 +5828,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -5886,21 +5853,22 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "devOptional": true, + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -5909,7 +5877,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5959,11 +5926,11 @@ } }, "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" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6032,6 +5999,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -6039,21 +6007,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -6065,7 +6022,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "optional": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -6091,20 +6047,10 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "optional": true, "bin": { "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6115,7 +6061,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", - "optional": true, "dependencies": { "camel-case": "^3.0.0", "clean-css": "^4.2.1", @@ -6135,14 +6080,12 @@ "node_modules/html-minifier/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "optional": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/html-to-text": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", - "optional": true, "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", @@ -6165,7 +6108,6 @@ "url": "https://github.com/sponsors/fb55" } ], - "optional": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -6212,7 +6154,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -6238,9 +6179,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { "parent-module": "^1.0.0", @@ -6300,8 +6241,7 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inquirer": { "version": "8.2.6", @@ -6347,7 +6287,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6356,10 +6295,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "devOptional": true, + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { "hasown": "^2.0.2" }, @@ -6374,7 +6312,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "optional": true, "bin": { "is-docker": "cli.js" }, @@ -6389,7 +6326,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "optional": true, "dependencies": { "acorn": "^7.1.1", "object-assign": "^4.1.1" @@ -6399,7 +6335,6 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "optional": true, "bin": { "acorn": "bin/acorn" }, @@ -6411,7 +6346,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -6437,7 +6371,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6458,7 +6391,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, "engines": { "node": ">=0.12.0" } @@ -6475,17 +6407,17 @@ "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "optional": true + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "optional": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -6522,7 +6454,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "optional": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -6530,11 +6461,6 @@ "node": ">=8" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -6624,13 +6550,15 @@ } }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "devOptional": true, + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": ">=14" + }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -7347,35 +7275,66 @@ "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/js-beautify": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", - "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", - "optional": true, + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.3.3", - "js-cookie": "^3.0.5", - "nopt": "^7.2.0" + "@isaacs/cliui": "^8.0.2" }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" + "funding": { + "url": "https://github.com/sponsors/isaacs" }, - "engines": { - "node": ">=14" + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "optional": true, "engines": { "node": ">=14" } @@ -7383,8 +7342,7 @@ "node_modules/js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", - "optional": true + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -7404,15 +7362,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -7494,7 +7452,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", - "optional": true, "dependencies": { "is-promise": "^2.0.0", "promise": "^7.0.1" @@ -7504,7 +7461,6 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", - "optional": true, "dependencies": { "cheerio": "1.0.0-rc.12", "commander": "^6.1.0", @@ -7523,17 +7479,16 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "optional": true, "engines": { "node": ">= 6" } }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } @@ -7577,7 +7532,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", - "optional": true, "funding": { "url": "https://ko-fi.com/killymxi" } @@ -7607,26 +7561,23 @@ "node_modules/libbase64": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", - "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", - "optional": true + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==" }, "node_modules/libmime": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.5.tgz", - "integrity": "sha512-nSlR1yRZ43L3cZCiWEw7ali3jY29Hz9CQQ96Oy+sSspYnIP5N54ucOPHqooBsXzwrX1pwn13VUE05q4WmzfaLg==", - "optional": true, + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.6.tgz", + "integrity": "sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA==", "dependencies": { - "encoding-japanese": "2.1.0", + "encoding-japanese": "2.2.0", "iconv-lite": "0.6.3", "libbase64": "1.3.0", - "libqp": "2.1.0" + "libqp": "2.1.1" } }, "node_modules/libmime/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -7635,15 +7586,14 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.11.tgz", - "integrity": "sha512-mF3KaORjJQR6JBNcOkluDcJKhtoQT4VTLRMrX1v/wlBayL4M8ybwEDeryyPcrSEJmD0rVwHUbBarpZwN5NfPFQ==" + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz", + "integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==" }, "node_modules/libqp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.0.tgz", - "integrity": "sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A==", - "optional": true + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -7655,40 +7605,10 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "optional": true, "dependencies": { "uc.micro": "^2.0.0" } }, - "node_modules/liquidjs": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.17.0.tgz", - "integrity": "sha512-M4MC5/nencttIJHirl5jFTkl7Yu+grIDLn3Qgl7BPAD3BsbTCQknDxlG5VXWRwslWIjk8lSZZjVq9LioILDk1Q==", - "optional": true, - "dependencies": { - "commander": "^10.0.0" - }, - "bin": { - "liquid": "bin/liquid.js", - "liquidjs": "bin/liquid.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/liquidjs" - } - }, - "node_modules/liquidjs/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -7784,8 +7704,7 @@ "node_modules/lower-case": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", - "optional": true + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" }, "node_modules/lru-cache": { "version": "5.1.1", @@ -7809,28 +7728,26 @@ } }, "node_modules/mailparser": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.1.tgz", - "integrity": "sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==", - "optional": true, + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.3.tgz", + "integrity": "sha512-0RM14cZF0gO1y2Q/82hhWranispZOUSYHwvQ21h12x90NwD6+D5q59S5nOLqCtCdYitHN58LJXWEHa4RWm7BYA==", "dependencies": { - "encoding-japanese": "2.1.0", + "encoding-japanese": "2.2.0", "he": "1.2.0", "html-to-text": "9.0.5", "iconv-lite": "0.6.3", - "libmime": "5.3.5", + "libmime": "5.3.6", "linkify-it": "5.0.0", - "mailsplit": "5.4.0", - "nodemailer": "6.9.13", + "mailsplit": "5.4.3", + "nodemailer": "7.0.3", "punycode.js": "2.3.1", - "tlds": "1.252.0" + "tlds": "1.259.0" } }, "node_modules/mailparser/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -7838,71 +7755,16 @@ "node": ">=0.10.0" } }, - "node_modules/mailparser/node_modules/nodemailer": { - "version": "6.9.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", - "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", - "optional": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/mailsplit": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.0.tgz", - "integrity": "sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==", - "optional": true, - "dependencies": { - "libbase64": "1.2.1", - "libmime": "5.2.0", - "libqp": "2.0.1" - } - }, - "node_modules/mailsplit/node_modules/encoding-japanese": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz", - "integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==", - "optional": true, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/mailsplit/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mailsplit/node_modules/libbase64": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz", - "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==", - "optional": true - }, - "node_modules/mailsplit/node_modules/libmime": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.0.tgz", - "integrity": "sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==", - "optional": true, + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.3.tgz", + "integrity": "sha512-PFV0BBh4Tv7Omui5FtXXVtN4ExAxIi8Yvmb9JgBz+J6Hnnrv/YYXLlKKudLhXwd3/qWEATOslRsnzVCWDeCnmQ==", "dependencies": { - "encoding-japanese": "2.0.0", - "iconv-lite": "0.6.3", - "libbase64": "1.2.1", - "libqp": "2.0.1" + "libbase64": "1.3.0", + "libmime": "5.3.6", + "libqp": "2.1.1" } }, - "node_modules/mailsplit/node_modules/libqp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.0.1.tgz", - "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==", - "optional": true - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -7933,6 +7795,14 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7961,8 +7831,7 @@ "node_modules/mensch": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", - "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", - "optional": true + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==" }, "node_modules/merge-descriptors": { "version": "1.0.3", @@ -8093,7 +7962,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.15.3.tgz", "integrity": "sha512-bW2WpJxm6HS+S3Yu6tq1DUPFoTxU9sPviUSmnL7Ua+oVO3WA5ILFWqvujUlz+oeuM+HCwEyMiP5xvKNPENVjYA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "mjml-cli": "4.15.3", @@ -8110,7 +7978,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.15.3.tgz", "integrity": "sha512-LPNVSj1LyUVYT9G1gWwSw3GSuDzDsQCu0tPB2uDsq4VesYNnU6v3iLCQidMiR6azmIt13OEozG700ygAUuA6Ng==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8121,7 +7988,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.15.3.tgz", "integrity": "sha512-7pfUOVPtmb0wC+oUOn4xBsAw4eT5DyD6xqaxj/kssu6RrFXOXgJaVnDPAI9AzIvXJ/5as9QrqRGYAddehwWpHQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8132,7 +7998,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.15.3.tgz", "integrity": "sha512-79qwn9AgdGjJR1vLnrcm2rq2AsAZkKC5JPwffTMG+Nja6zGYpTDZFZ56ekHWr/r1b5WxkukcPj2PdevUug8c+Q==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8143,7 +8008,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.15.3.tgz", "integrity": "sha512-3ju6I4l7uUhPRrJfN3yK9AMsfHvrYbRkcJ1GRphFHzUj37B2J6qJOQUpzA547Y4aeh69TSb7HFVf1t12ejQxVw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8154,7 +8018,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.15.3.tgz", "integrity": "sha512-+V2TDw3tXUVEptFvLSerz125C2ogYl8klIBRY1m5BHd4JvGVf3yhx8N3PngByCzA6PGcv/eydGQN+wy34SHf0Q==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "chokidar": "^3.0.0", @@ -8177,7 +8040,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.15.3.tgz", "integrity": "sha512-hYdEFdJGHPbZJSEysykrevEbB07yhJGSwfDZEYDSbhQQFjV2tXrEgYcFD5EneMaowjb55e3divSJxU4c5q4Qgw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8188,7 +8050,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.15.3.tgz", "integrity": "sha512-Dmwk+2cgSD9L9GmTbEUNd8QxkTZtW9P7FN/ROZW/fGZD6Hq6/4TB0zEspg2Ow9eYjZXO2ofOJ3PaQEEShKV0kQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "cheerio": "1.0.0-rc.12", @@ -8206,7 +8067,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.15.3.tgz", "integrity": "sha512-vh27LQ9FG/01y0b9ntfqm+GT5AjJnDSDY9hilss2ixIUh0FemvfGRfsGVeV5UBVPBKK7Ffhvfqc7Rciob9Spzw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8217,7 +8077,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.15.3.tgz", "integrity": "sha512-HSu/rKnGZVKFq3ciT46vi1EOy+9mkB0HewO4+P6dP/Y0UerWkN6S3UK11Cxsj0cAp0vFwkPDCdOeEzRdpFEkzA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8228,7 +8087,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.15.3.tgz", "integrity": "sha512-o3mRuuP/MB5fZycjD3KH/uXsnaPl7Oo8GtdbJTKtH1+O/3pz8GzGMkscTKa97l03DAG2EhGrzzLcU2A6eshwFw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8239,7 +8097,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.15.3.tgz", "integrity": "sha512-2ISo0r5ZKwkrvJgDou9xVPxxtXMaETe2AsAA02L89LnbB2KC0N5myNsHV0sEysTw9+CfCmgjAb0GAI5QGpxKkQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8250,7 +8107,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.15.3.tgz", "integrity": "sha512-Eo56FA5C2v6ucmWQL/JBJ2z641pLOom4k0wP6CMZI2utfyiJ+e2Uuinj1KTrgDcEvW4EtU9HrfAqLK9UosLZlg==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8261,7 +8117,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.15.3.tgz", "integrity": "sha512-CzV2aDPpiNIIgGPHNcBhgyedKY4SX3BJoTwOobSwZVIlEA6TAWB4Z9WwFUmQqZOgo1AkkiTHPZQvGcEhFFXH6g==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8272,7 +8127,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.15.3.tgz", "integrity": "sha512-MDNDPMBOgXUZYdxhosyrA2kudiGO8aogT0/cODyi2Ed9o/1S7W+je11JUYskQbncqhWKGxNyaP4VWa+6+vUC/g==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8283,7 +8137,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.15.3.tgz", "integrity": "sha512-J2PxCefUVeFwsAExhrKo4lwxDevc5aKj888HBl/wN4EuWOoOg06iOGCxz4Omd8dqyFsrqvbBuPqRzQ+VycGmaA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8294,7 +8147,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.15.3.tgz", "integrity": "sha512-9J+JuH+mKrQU65CaJ4KZegACUgNIlYmWQYx3VOBR/tyz+8kDYX7xBhKJCjQ1I4wj2Tvga3bykd89Oc2kFZ5WOw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8305,7 +8157,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.15.3.tgz", "integrity": "sha512-IM59xRtsxID4DubQ0iLmoCGXguEe+9BFG4z6y2xQDrscIa4QY3KlfqgKGT69ojW+AVbXXJPEVqrAi4/eCsLItQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8316,7 +8167,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.15.3.tgz", "integrity": "sha512-9cLAPuc69yiuzNrMZIN58j+HMK1UWPaq2i3/Fg2ZpimfcGFKRcPGCbEVh0v+Pb6/J0+kf8yIO0leH20opu3AyQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8327,7 +8177,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.15.3.tgz", "integrity": "sha512-g1OhSdofIytE9qaOGdTPmRIp7JsCtgO0zbsn1Fk6wQh2gEL55Z40j/VoghslWAWTgT2OHFdBKnMvWtN6U5+d2Q==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8338,7 +8187,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.15.3.tgz", "integrity": "sha512-sr/+35RdxZroNQVegjpfRHJ5hda9XCgaS4mK2FGO+Mb1IUevKfeEPII3F/cHDpNwFeYH3kAgyqQ22ClhGLWNBA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "js-beautify": "^1.6.14", @@ -8355,7 +8203,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.15.3.tgz", "integrity": "sha512-VsKH/Jdlf8Yu3y7GpzQV5n7JMdpqvZvTSpF6UQXL0PWOm7k6+LX+sCZimOfpHJ+wCaaybpxokjWZ71mxOoCWoA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8366,7 +8213,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.15.3.tgz", "integrity": "sha512-Tz0UX8/JVYICLjT+U8J1f/TFxIYVYjzZHeh4/Oyta0pLpRLeZlxEd71f3u3kdnulCKMP4i37pFRDmyLXAlEuLw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "detect-node": "2.1.0", @@ -8385,7 +8231,6 @@ "url": "https://github.com/sponsors/fb55" } ], - "optional": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -8397,7 +8242,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.15.3.tgz", "integrity": "sha512-1zZS8P4O0KweWUqNS655+oNnVMPQ1Rq1GaZq5S9JfwT1Vh/m516lSmiTW9oko6gGHytt5s6Yj6oOeu5Zm8FoLw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "mjml-accordion": "4.15.3", @@ -8431,7 +8275,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.15.3.tgz", "integrity": "sha512-IGyHheOYyRchBLiAEgw3UM11kFNmBSMupu2BDdejC6ZiDhEAdG+tyERlsCwDPYtXanvFpGWULIu3XlsUPc+RZw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8442,7 +8285,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.15.3.tgz", "integrity": "sha512-JfVPRXH++Hd933gmQfG8JXXCBCR6fIzC3DwiYycvanL/aW1cEQ2EnebUfQkt5QzlYjOkJEH+JpccAsq3ln6FZQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8453,7 +8295,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.15.3.tgz", "integrity": "sha512-7sD5FXrESOxpT9Z4Oh36bS6u/geuUrMP1aCg2sjyAwbPcF1aWa2k9OcatQfpRf6pJEhUZ18y6/WBBXmMVmSzXg==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8464,7 +8305,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.15.3.tgz", "integrity": "sha512-3B7Qj+17EgDdAtZ3NAdMyOwLTX1jfmJuY7gjyhS2HtcZAmppW+cxqHUBwCKfvSRgTQiccmEvtNxaQK+tfyrZqA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8475,7 +8315,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.15.3.tgz", "integrity": "sha512-FLx7DcRKTdKdcOCbMyBaeudeHaHpwPveRrBm6WyQe3LXx6FfdmOh59i71/16LFQMgBOD3N4/UJkzxLzlTJzMqQ==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8486,7 +8325,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.15.3.tgz", "integrity": "sha512-+C0hxCmw9kg0XzT6vhE5mFkK6y225nC8UEQcN94K0fBCjPKkM+HqZMwGX205fzdGRi+Bxa55b/VhrIVwdv+8vw==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8497,7 +8335,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.15.3.tgz", "integrity": "sha512-Xb72KdqRwjv/qM2rJpV22syyP2N3cRQ9VVDrN6u2FSzLq02buFNxmSPJ7CKhat3PrUNdVHU75KZwOf/tz4UEhA==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9" } @@ -8506,7 +8343,6 @@ "version": "4.15.3", "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.15.3.tgz", "integrity": "sha512-ditsCijeHJrmBmObtJmQ18ddLxv5oPyMTdPU8Di8APOnD2zPk7Z4UAuJSl7HXB45oFiivr3MJf4koFzMUSZ6Gg==", - "optional": true, "dependencies": { "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", @@ -8526,12 +8362,12 @@ } }, "node_modules/mongodb": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz", - "integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz", + "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.1", + "bson": "^6.10.3", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -8579,45 +8415,14 @@ "whatwg-url": "^14.1.0 || ^13.0.0" } }, - "node_modules/mongodb-connection-string-url/node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", - "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", - "dependencies": { - "tr46": "^5.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/mongoose": { - "version": "8.10.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.10.1.tgz", - "integrity": "sha512-5beTeBZnJNndRXU9rxPol0JmTWZMAtgkPbooROkGilswvrZALDERY4cJrGZmgGwDS9dl0mxiB7si+Mv9Yms2fg==", + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.15.1.tgz", + "integrity": "sha512-RhQ4DzmBi5BNGcS0w4u1vdMRIKcteXTCNzDt1j7XRcdWYBz1MjMjulBhPaeC5jBCHOD1yinuOFTTSOWLLGexWw==", "dependencies": { - "bson": "^6.10.1", + "bson": "^6.10.3", "kareem": "2.6.3", - "mongodb": "~6.13.0", + "mongodb": "~6.16.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -8656,20 +8461,20 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multer": { - "version": "1.4.4-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", - "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", + "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", "dependencies": { "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "type-is": "^1.6.18", + "xtend": "^4.0.2" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 10.16.0" } }, "node_modules/mute-stream": { @@ -8701,14 +8506,12 @@ "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "optional": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node_modules/no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "optional": true, "dependencies": { "lower-case": "^1.1.1" } @@ -8720,9 +8523,9 @@ "dev": true }, "node_modules/node-addon-api": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", - "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", "engines": { "node": "^18 || ^20 || >= 21" } @@ -8755,10 +8558,29 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp-build": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", - "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -8778,9 +8600,9 @@ "dev": true }, "node_modules/nodemailer": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", - "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", + "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", "engines": { "node": ">=6.0.0" } @@ -8789,7 +8611,6 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "optional": true, "dependencies": { "abbrev": "^2.0.0" }, @@ -8804,7 +8625,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -8825,7 +8645,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "optional": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -8850,9 +8669,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "engines": { "node": ">= 0.4" }, @@ -8899,7 +8718,6 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "optional": true, "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -8964,7 +8782,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", - "optional": true, "dependencies": { "p-timeout": "^3.1.0" }, @@ -8979,7 +8796,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "optional": true, "engines": { "node": ">=4" } @@ -9018,7 +8834,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "optional": true, "dependencies": { "p-finally": "^1.0.0" }, @@ -9039,7 +8854,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", - "optional": true, "dependencies": { "p-timeout": "^3.0.0" }, @@ -9051,16 +8865,14 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "devOptional": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", - "optional": true, "dependencies": { "no-case": "^2.2.0" } @@ -9096,35 +8908,43 @@ } }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "optional": true, + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dependencies": { - "entities": "^4.4.0" + "entities": "^6.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "optional": true, + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dependencies": { - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "parse5": "^7.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", - "optional": true, "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" @@ -9204,8 +9024,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "devOptional": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", @@ -9250,7 +9069,6 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", - "optional": true, "funding": { "url": "https://ko-fi.com/killymxi" } @@ -9274,9 +9092,9 @@ } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "engines": { "node": ">= 6" @@ -9365,9 +9183,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -9418,34 +9236,41 @@ } }, "node_modules/preview-email": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.1.0.tgz", - "integrity": "sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw==", - "optional": true, + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz", + "integrity": "sha512-DBS3Nir18YtKc8loYCCOGitmiaQ0vTdahPoiXxwNweJDpmVZo+w3tppufOhoK0m8skpRxT56llYLs3VrORnmNQ==", "dependencies": { "ci-info": "^3.8.0", "display-notification": "2.0.0", "fixpack": "^4.0.0", "get-port": "5.1.1", - "mailparser": "^3.7.1", - "nodemailer": "^6.9.13", + "mailparser": "^3.6.4", + "nodemailer": "^6.9.2", "open": "7", "p-event": "4.2.0", "p-wait-for": "3.2.0", - "pug": "^3.0.3", - "uuid": "^9.0.1" + "pug": "^3.0.2", + "uuid": "^9.0.0" }, "engines": { "node": ">=14" } }, + "node_modules/preview-email/node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/prisma": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.20.0.tgz", - "integrity": "sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.20.0" + "@prisma/engines": "5.22.0" }, "bin": { "prisma": "build/index.js" @@ -9457,16 +9282,10 @@ "fsevents": "2.3.3" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, "node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "optional": true, "dependencies": { "asap": "~2.0.3" } @@ -9487,8 +9306,7 @@ "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "optional": true + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -9506,7 +9324,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", - "optional": true, "dependencies": { "pug-code-gen": "^3.0.3", "pug-filters": "^4.0.0", @@ -9522,7 +9339,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "optional": true, "dependencies": { "constantinople": "^4.0.1", "js-stringify": "^1.0.2", @@ -9533,7 +9349,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz", "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==", - "optional": true, "dependencies": { "constantinople": "^4.0.1", "doctypes": "^1.1.0", @@ -9548,14 +9363,12 @@ "node_modules/pug-error": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", - "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", - "optional": true + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==" }, "node_modules/pug-filters": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "optional": true, "dependencies": { "constantinople": "^4.0.1", "jstransformer": "1.0.0", @@ -9568,7 +9381,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", - "optional": true, "dependencies": { "character-parser": "^2.2.0", "is-expression": "^4.0.0", @@ -9579,7 +9391,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "optional": true, "dependencies": { "pug-error": "^2.0.0", "pug-walk": "^2.0.0" @@ -9589,7 +9400,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "optional": true, "dependencies": { "object-assign": "^4.1.1", "pug-walk": "^2.0.0" @@ -9599,7 +9409,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "optional": true, "dependencies": { "pug-error": "^2.0.0", "token-stream": "1.0.0" @@ -9608,14 +9417,12 @@ "node_modules/pug-runtime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", - "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", - "optional": true + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" }, "node_modules/pug-strip-comments": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "optional": true, "dependencies": { "pug-error": "^2.0.0" } @@ -9623,8 +9430,7 @@ "node_modules/pug-walk": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", - "optional": true + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, "node_modules/punycode": { "version": "2.3.1", @@ -9638,7 +9444,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "optional": true, "engines": { "node": ">=6" } @@ -9728,7 +9533,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -9743,7 +9547,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -9755,29 +9558,22 @@ "dev": true }, "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -9789,7 +9585,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, "engines": { "node": ">=8.6" }, @@ -9802,17 +9597,10 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "optional": true - }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "optional": true, "engines": { "node": ">= 0.10" } @@ -9830,7 +9618,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -9845,18 +9632,20 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "devOptional": true, + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9892,9 +9681,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "engines": { "node": ">=10" @@ -9920,9 +9709,9 @@ "dev": true }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "engines": { "iojs": ">=1.0.0", @@ -9992,7 +9781,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-3.2.0.tgz", "integrity": "sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==", - "optional": true, "dependencies": { "execa": "^0.10.0" }, @@ -10001,10 +9789,9 @@ } }, "node_modules/run-applescript/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "optional": true, + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -10020,7 +9807,6 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "optional": true, "dependencies": { "cross-spawn": "^6.0.0", "get-stream": "^3.0.0", @@ -10038,7 +9824,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "optional": true, "engines": { "node": ">=4" } @@ -10047,7 +9832,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -10056,7 +9840,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "optional": true, "dependencies": { "path-key": "^2.0.0" }, @@ -10068,7 +9851,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "optional": true, "engines": { "node": ">=4" } @@ -10077,7 +9859,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "optional": true, "bin": { "semver": "bin/semver" } @@ -10086,7 +9867,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "optional": true, "dependencies": { "shebang-regex": "^1.0.0" }, @@ -10098,7 +9878,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -10106,14 +9885,12 @@ "node_modules/run-applescript/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "optional": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/run-applescript/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "optional": true, "dependencies": { "isexe": "^2.0.0" }, @@ -10154,9 +9931,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dependencies": { "tslib": "^2.1.0" } @@ -10238,7 +10015,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", - "optional": true, "dependencies": { "parseley": "^0.12.0" }, @@ -10247,9 +10023,9 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "bin": { "semver": "bin/semver.js" }, @@ -10328,6 +10104,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10365,14 +10142,65 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -10416,21 +10244,20 @@ "version": "1.12.2", "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", - "optional": true, "engines": { "node": "*" } }, "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.2", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, @@ -10447,6 +10274,22 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -10459,6 +10302,38 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -10539,18 +10414,13 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10627,7 +10497,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -10653,10 +10522,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.1.tgz", + "integrity": "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", "dev": true, "dependencies": { "component-emitter": "^1.3.0", @@ -10664,7 +10548,7 @@ "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" @@ -10686,13 +10570,13 @@ } }, "node_modules/supertest": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", - "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", "dev": true, "dependencies": { "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.2.1" }, "engines": { "node": ">=14.18.0" @@ -10713,7 +10597,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "devOptional": true, "engines": { "node": ">= 0.4" }, @@ -10736,38 +10619,37 @@ } }, "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "@pkgr/core": "^0.2.4" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/synckit" } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/terser": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz", - "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz", + "integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -10779,16 +10661,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -10826,6 +10708,25 @@ "node": ">= 10.13.0" } }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser-webpack-plugin/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -10917,10 +10818,9 @@ "dev": true }, "node_modules/tlds": { - "version": "1.252.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.252.0.tgz", - "integrity": "sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==", - "optional": true, + "version": "1.259.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz", + "integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==", "bin": { "tlds": "bin.js" } @@ -10943,20 +10843,10 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "devOptional": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, "dependencies": { "is-number": "^7.0.0" }, @@ -10975,13 +10865,34 @@ "node_modules/token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", - "optional": true + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==" + }, + "node_modules/token-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/tree-kill": { "version": "1.2.2", @@ -10993,9 +10904,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "engines": { "node": ">=16" @@ -11005,9 +10916,9 @@ } }, "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.3.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", + "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", "dev": true, "dependencies": { "bs-logger": "^0.2.6", @@ -11017,7 +10928,8 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.2", + "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -11052,10 +10964,22 @@ } } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -11154,9 +11078,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-check": { "version": "0.4.0", @@ -11209,9 +11133,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -11224,14 +11148,12 @@ "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "optional": true + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -11250,10 +11172,21 @@ "node": ">=8" } }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, "node_modules/universalify": { "version": "2.0.1", @@ -11273,9 +11206,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -11305,8 +11238,7 @@ "node_modules/upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", - "optional": true + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" }, "node_modules/uri-js": { "version": "4.4.1", @@ -11338,7 +11270,6 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "optional": true, "bin": { "uuid": "dist/bin/uuid" } @@ -11367,15 +11298,14 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", - "optional": true, "engines": { "node": ">=10" } }, "node_modules/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", "engines": { "node": ">= 0.10" } @@ -11392,7 +11322,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -11407,9 +11336,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -11432,7 +11361,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", - "optional": true, "dependencies": { "ansi-colors": "^4.1.1", "escape-goat": "^3.0.0", @@ -11449,7 +11377,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "optional": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -11463,7 +11390,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "optional": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -11478,7 +11404,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "optional": true, "dependencies": { "domelementtype": "^2.0.1" }, @@ -11493,7 +11418,6 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "optional": true, "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -11507,7 +11431,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "optional": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -11522,7 +11445,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "optional": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -11531,7 +11453,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", - "optional": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^3.3.0", @@ -11546,7 +11467,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "optional": true, "bin": { "mime": "cli.js" }, @@ -11555,18 +11475,23 @@ } }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", @@ -11583,9 +11508,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, @@ -11615,9 +11540,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", + "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", "dev": true, "engines": { "node": ">=10.13.0" @@ -11628,6 +11553,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -11641,17 +11567,41 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { @@ -11672,7 +11622,6 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "optional": true, "dependencies": { "@babel/parser": "^7.9.6", "@babel/types": "^7.9.6", @@ -11786,7 +11735,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "devOptional": true, "engines": { "node": ">=10" } @@ -11801,7 +11749,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "devOptional": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -11819,7 +11766,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "devOptional": true, "engines": { "node": ">=12" } diff --git a/package.json b/package.json index 1a5c217..fde9eda 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "integration:full": "docker compose up -d && sleep 5s && npm run test:migrate && npm run test:integration && docker compose down" }, "dependencies": { - "@nestjs-modules/mailer": "^2.0.2", + "@nestjs-modules/mailer": "^1.6.1", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index b7f0675..dd897b6 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,32 +1,34 @@ import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { APP_GUARD } from '@nestjs/core'; +import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { UserModule } from './user/user.module'; +import { AuctionModule } from './auction/auction.module'; import { AuthModule } from './auth/auth.module'; -import { PrismaModule } from './prisma/prisma.module'; import { JwtGuard } from './auth/guard/jwt.guard'; -import { APP_GUARD } from '@nestjs/core'; import { ChatModule } from './chat/chat.module'; +import { FieldModule } from './field/field.module'; import { GameModule } from './game/game.module'; +import { PaymentModule } from './payment/payment.module'; import { PlayerModule } from './player/player.module'; -import { EventEmitterModule } from '@nestjs/event-emitter'; -import { WebSocketServerModule } from './webSocketServer/webSocketServer.module'; -import { WebSocketServerService } from './webSocketServer/webSocketServer.service'; -import { MongooseModule } from '@nestjs/mongoose'; -import { AuctionModule } from './auction/auction.module'; +import { PrismaModule } from './prisma/prisma.module'; +import { SecretModule } from './secret/secret.module'; +import { UserModule } from './user/user.module'; +import { WebSocketProviderModule } from './webSocketProvider/webSocketProvider.module'; +import { WebSocketProvider } from './webSocketProvider/webSocketProvider.service'; +import { TradeModule } from './trade/trade.module'; @Module({ imports: [ ConfigModule.forRoot(), - EventEmitterModule.forRoot(), UserModule, AuthModule, PrismaModule, ChatModule, GameModule, PlayerModule, - WebSocketServerModule, + WebSocketProviderModule, MongooseModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], @@ -35,6 +37,10 @@ import { AuctionModule } from './auction/auction.module'; }), }), AuctionModule, + SecretModule, + FieldModule, + PaymentModule, + TradeModule, ], controllers: [AppController], providers: [ @@ -43,7 +49,7 @@ import { AuctionModule } from './auction/auction.module'; provide: APP_GUARD, useClass: JwtGuard, }, - WebSocketServerService, + WebSocketProvider, ], }) export class AppModule {} diff --git a/src/auction/auction.module.ts b/src/auction/auction.module.ts index 824d1ae..627e0e2 100644 --- a/src/auction/auction.module.ts +++ b/src/auction/auction.module.ts @@ -2,9 +2,18 @@ import { forwardRef, Module } from '@nestjs/common'; import { AuctionService } from './auction.service'; import { GameModule } from 'src/game/game.module'; import { PlayerModule } from 'src/player/player.module'; +import { TimerModule } from 'src/timer/timers.module'; +import { FieldModule } from 'src/field/field.module'; +import { WebSocketProviderModule } from 'src/webSocketProvider/webSocketProvider.module'; @Module({ - imports: [forwardRef(() => GameModule), forwardRef(() => PlayerModule)], + imports: [ + forwardRef(() => GameModule), + forwardRef(() => PlayerModule), + TimerModule, + FieldModule, + WebSocketProviderModule, + ], providers: [AuctionService], exports: [AuctionService], }) diff --git a/src/auction/auction.service.ts b/src/auction/auction.service.ts index ee3ffef..bd37ef1 100644 --- a/src/auction/auction.service.ts +++ b/src/auction/auction.service.ts @@ -3,16 +3,22 @@ import { WsException } from '@nestjs/websockets'; import { GamePayload } from 'src/game/game.repository'; import { GameService } from 'src/game/game.service'; import { Auction } from 'src/game/types/auction.type'; +import { TimerService } from 'src/timer/timers.service'; import { PlayerPayload } from 'src/player/player.repository'; import { PlayerService } from 'src/player/player.service'; +import { FieldService } from 'src/field/field.service'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; @Injectable() export class AuctionService { constructor( + @Inject(forwardRef(() => PlayerService)) + private playerService: PlayerService, @Inject(forwardRef(() => GameService)) private gameService: GameService, - @Inject(forwardRef(() => PlayerService)) - private playerService: PlayerService + private timerService: TimerService, + private fieldService: FieldService, + private webSocketProvider: WebSocketProvider ) { this.hightestInQueue = this.hightestInQueue.bind(this); this.winAuction = this.winAuction.bind(this); @@ -21,16 +27,17 @@ export class AuctionService { BID_TIME: number = 10000; MIN_RAISE: number = 100; auctions: Map = new Map(); - async putUpForAuction(game: Partial) { - const player = await this.getCurrentPlayerOrThrow(game); + async putUpForAuction(game: Partial, requestId: string) { + const player = await this.getCurrentPlayerOrThrow(game, requestId); const field = await this.getAuctionableFieldOrThrow( game.id, player.currentFieldIndex ); - this.gameService.clearTimer(game.id); + this.timerService.clear(game.id); const bidder = this.createInitialBidder(field.price); - const turnEnds = this.gameService.calculateEndOfTurn(this.BID_TIME); + const turnEnds = this.timerService.calculateFutureTime(this.BID_TIME); this.setAuction(game.id, { + bidTimeSec: this.BID_TIME / 1000, fieldIndex: field.index, bidders: [bidder], turnEnds, @@ -38,21 +45,31 @@ export class AuctionService { }); return this.getAuction(game.id); } - private async getCurrentPlayerOrThrow(game: Partial) { + private async getCurrentPlayerOrThrow( + game: Partial, + requestId?: string + ) { const player = await this.playerService.findByUserAndGameId( game.turnOfUserId, game.id ); if (!player) { - throw new WsException('No such player'); + throw new WsException({ message: 'No such player', requestId }); } return player; } - private async getAuctionableFieldOrThrow(gameId: string, fieldIndex: number) { - const fields = await this.gameService.getGameFields(gameId); - const field = this.gameService.findPlayerFieldByIndex(fields, fieldIndex); + private async getAuctionableFieldOrThrow( + gameId: string, + fieldIndex: number, + requestId?: string + ) { + const fields = await this.fieldService.getGameFields(gameId); + const field = this.fieldService.findPlayerFieldByIndex(fields, fieldIndex); if (!field.price) { - throw new WsException('You cant put this field to auction'); + throw new WsException({ + message: 'This field is not auctionable', + requestId, + }); } return field; } @@ -77,19 +94,31 @@ export class AuctionService { userId: string, raiseBy: number, bidAmount: number, - lastBid: number + lastBid: number, + requestId: string ) { const lastAcceptedBidderOrFirstBid = this.findLastAcceptedBidder(auction) || auction.bidders[0]; + console.log({ bidAmount, lastAcceptedBidderOrFirstBid }); if (bidAmount !== lastAcceptedBidderOrFirstBid.bid) - throw new WsException('Bid amount is not correct'); + throw new WsException({ + message: 'Bid amount is not correct', + requestId, + }); if (lastAcceptedBidderOrFirstBid.bid + raiseBy <= lastBid) - throw new WsException('Bid is not high enough'); - if (!auction) throw new WsException('Auction wasn’t started'); + throw new WsException({ + message: 'Your raise is not big enough', + requestId, + }); + if (!auction) + throw new WsException({ message: 'Auction wasn’t started', requestId }); if (raiseBy < this.MIN_RAISE) - throw new WsException('Raise is not big enough'); + throw new WsException({ message: 'Raise is too low', requestId }); if (auction.usersRefused.includes(userId)) - throw new WsException('You refused to auction'); + throw new WsException({ + message: 'You already refused to auction', + requestId, + }); } findLastAcceptedBidder(auction: Auction) { @@ -106,27 +135,46 @@ export class AuctionService { gameId: string, userId: string, raiseBy: number, - bidAmount: number + bidAmount: number, + requestId: string ) { const player = await this.playerService.findByUserAndGameId(userId, gameId); const auction = this.getAuction(gameId); const lastBidder = this.getLastBidder(auction); - this.playerService.validatePlayerMoney(player, lastBidder.bid); + this.playerService.validatePlayerMoney(player, lastBidder.bid, requestId); this.validateRaisePrice( auction, userId, raiseBy, bidAmount, - lastBidder.bid + lastBidder.bid, + requestId ); - this.gameService.clearTimer(gameId); + this.timerService.clear(gameId); this.setBidderOnAuction(gameId, userId, raiseBy, false); - return this.gameService.setTimer( + const auctionWithAcceptedBid = await this.timerService.set( gameId, - 2000, + 200, { gameId, userId, raiseBy }, this.hightestInQueue ); + + if (auctionWithAcceptedBid) { + this.webSocketProvider.server + .to(gameId) + .emit('raisedPrice', { auction: auctionWithAcceptedBid, requestId }); + this.timerService.set( + gameId, + 10000, + { ...auctionWithAcceptedBid, gameId }, + this.winAuction + ); + } else { + this.webSocketProvider.server.to(userId).emit('error', { + requestId, + message: 'Хтось одночасно поставив з вами і перебив вашу ставку', + }); + } } setBidderOnAuction( @@ -136,9 +184,9 @@ export class AuctionService { accepted: boolean ) { const auction = this.getAuction(gameId); - const biddder = this.findLastAcceptedBidder(auction); + const biddder = this.findLastAcceptedBidder(auction) || auction.bidders[0]; const newBid = biddder.bid + raiseBy; - const turnEnds = this.gameService.calculateEndOfTurn(this.BID_TIME); + const turnEnds = this.timerService.calculateFutureTime(this.BID_TIME); auction.bidders[auction.bidders.length] = { userId, bid: newBid, @@ -149,15 +197,24 @@ export class AuctionService { return auction; } - validateRefuseAuction(auction: Auction, player: Partial) { - if (!player) throw new WsException('No such player'); - if (!auction) throw new WsException('Auction wasn’t started'); + validateRefuseAuction( + auction: Auction, + player: Partial, + requestId: string + ) { + if (!player) + throw new WsException({ message: 'No such player', requestId }); + if (!auction) + throw new WsException({ message: 'No such auction', requestId }); const lastBidder = this.getLastBidder(auction); if (player.userId === lastBidder.userId) { - throw new WsException('You are the last bidder'); + throw new WsException({ message: 'You are the last bidder', requestId }); } if (auction.usersRefused.includes(player.userId)) - throw new WsException('You already refused to auction'); + throw new WsException({ + message: 'You already refused to auction', + requestId, + }); } private addPlayerRefusal(auction: Auction, userId: string): void { @@ -169,31 +226,27 @@ export class AuctionService { return game.players.filter((player) => !player.lost); } - async refuseAuction(gameId: string, userId: string) { + async refuseAuction(gameId: string, userId: string, requestId: string) { const player = await this.playerService.findByUserAndGameId(userId, gameId); const auction = this.getAuction(gameId); const game = player.game; - this.validateRefuseAuction(auction, player); + this.validateRefuseAuction(auction, player, requestId); this.addPlayerRefusal(auction, userId); const activePlayers = this.getActivePlayers(game); const isAuctionComplete = auction.usersRefused.length >= activePlayers.length; if (isAuctionComplete) { - this.gameService.clearTimer(gameId); + this.timerService.clear(gameId); const hasWinner = !!this.findLastAcceptedBidder(auction); - return { - auction, - hasWinner, - finished: true, - game, - }; + if (hasWinner) { + this.winAuction({ ...auction, gameId }); + } else { + this.gameService.passTurn(game); + } } - return { - auction, - hasWinner: false, - finished: false, - game, - }; + this.webSocketProvider.server + .to(gameId) + .emit('refusedFromAuction', { auction, requestId }); } hightestInQueue(args: { gameId: string; userId: string; raiseBy: number }) { @@ -207,8 +260,17 @@ export class AuctionService { } async winAuction(auction: Auction & { gameId: string }) { - const fields = await this.gameService.getGameFields(auction.gameId); - const field = this.gameService.findPlayerFieldByIndex( + const { updatedPlayer, fields } = + await this.processVictoryOfAuction(auction); + this.webSocketProvider.server + .to(auction.gameId) + .emit('wonAuction', { auction, game: updatedPlayer.game, fields }); + this.gameService.passTurn(updatedPlayer.game); + } + + async processVictoryOfAuction(auction: Auction & { gameId: string }) { + const fields = await this.fieldService.getGameFields(auction.gameId); + const field = this.fieldService.findPlayerFieldByIndex( fields, auction.fieldIndex ); @@ -220,7 +282,7 @@ export class AuctionService { auction.gameId, lastAcceptedBidder.bid ); - await this.gameService.updateFields(fields, ['ownedBy']); + await this.fieldService.updateFields(fields, ['ownedBy']); this.setAuction(auction.gameId, null); return { updatedPlayer, fields }; } diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 51fd4c2..19f5477 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -8,10 +8,19 @@ import { Patch, Post, Res, + Sse, UseGuards, + UseInterceptors, } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { Response } from 'express'; +import { Observable } from 'rxjs'; +import { SseService } from 'src/sse/sse.service'; import { AuthService } from './auth.service'; +import { GetUser } from './decorator'; +import { GetCurrentUserId } from './decorator/get-current-user-id.decorator'; +import { GetCurrentUser } from './decorator/get-current-user.decorator'; +import { Public } from './decorator/public.decorator'; import { ChangePasswordDto, ForgotPasswordDto, @@ -19,102 +28,43 @@ import { SignInDto, SignUpDto, } from './dto'; -import { AuthGuard } from '@nestjs/passport'; -import { GetCurrentUser } from './decorator/get-current-user.decorator'; -import { GetCurrentUserId } from './decorator/get-current-user-id.decorator'; -import { Public } from './decorator/public.decorator'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ConfigService } from '@nestjs/config'; -import { AccessCookieAttributes, RefreshCookieAttributes } from './types'; import { JwtRtGuard } from './guard'; -import { GetUser } from './decorator'; +import { TokenCookieInterceptor } from './interceptor/authCookies.interceptor'; import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; @ApiTags('auth') @Controller('auth') export class AuthController { - constructor(private authService: AuthService) {} - static readonly ACCESS_COOKIES_ATTRIBUTES: AccessCookieAttributes = { - httpOnly: true, - sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', - secure: process.env.NODE_ENV === 'production', - ...(process.env.NODE_ENV === 'production' && { - domain: process.env.DOMAIN_NAME_PROD, - }), - }; - - static readonly REFRESH_COOKIES_ATTRIBUTES: RefreshCookieAttributes = { - httpOnly: true, - path: '/', - sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', - secure: process.env.NODE_ENV === 'production', - ...(process.env.NODE_ENV === 'production' && { - domain: process.env.DOMAIN_NAME_PROD, - }), - }; - - setCookie( - res: Response, - name: string, - value: string, - options: Record - ) { - const cookieOptions = { - ...options, - }; - res.cookie(name, value, cookieOptions); - } - - setTokens(res: Response, accessToken: string, refreshToken: string) { - this.setCookie( - res, - 'access_token', - accessToken, - AuthController.ACCESS_COOKIES_ATTRIBUTES - ); - this.setCookie( - res, - 'refresh_token', - refreshToken, - AuthController.REFRESH_COOKIES_ATTRIBUTES - ); - } - + constructor( + private authService: AuthService, + private sseService: SseService + ) {} @Public() @Post('/local/signup') @ApiOperation({ summary: 'signup' }) async signup(@Body() dto: SignUpDto) { - await this.authService.signup(dto); + const user = await this.authService.signup(dto); - return { status: 'success', message: 'Confirm you email' }; + return { status: 'success', message: 'Confirm you email', user }; } @Public() @Post('/local/signin') + @UseInterceptors(TokenCookieInterceptor) @ApiOperation({ summary: 'signin' }) - async signin(@Body() dto: SignInDto, @Res() res: Response) { + async signin(@Body() dto: SignInDto) { const { tokens, user } = await this.authService.signin(dto); - const { access_token: accessToken, refresh_token: refreshToken } = tokens; - this.setTokens(res, accessToken, refreshToken); - return res - .status(HttpStatus.OK) - .send({ status: 'success', message: 'Logged in successfully', user }); + return { + status: 'success', + message: 'Logged in successfully', + user, + ...tokens, + }; } - @Public() - @UseGuards(JwtRtGuard) @Get('local/me') async me(@GetUser() user: JwtPayloadWithRt, @Res() res: Response) { const result = await this.authService.me(user); - - this.setCookie( - res, - 'access_token', - result.access_token, - AuthController.ACCESS_COOKIES_ATTRIBUTES - ); - delete result.access_token; - return res.status(HttpStatus.OK).send(result); } @@ -127,37 +77,49 @@ export class AuthController { } @Public() - @UseGuards(AuthGuard('jwt-refresh')) + @UseGuards(JwtRtGuard) @Post('refresh') @ApiOperation({ summary: 'refreshTokens' }) + @UseInterceptors(TokenCookieInterceptor) async refreshTokens( @GetCurrentUserId() userId: string, - @GetCurrentUser('refreshToken') refreshToken: string, - @Res() res: Response + @GetCurrentUser('refreshToken') refreshTokenOld: string ) { - const { access_token: accessToken, refresh_token: refreshTokenNew } = - await this.authService.refreshTokens(userId, refreshToken); - - this.setTokens(res, accessToken, refreshTokenNew); - - return res - .status(HttpStatus.OK) - .send({ status: 'success', message: 'Refreshed token successfully' }); + const tokens = await this.authService.refreshTokens( + userId, + refreshTokenOld + ); + return { + status: 'success', + message: 'Refreshed token successfully', + ...tokens, + }; } @Public() @Get('confirm-email/:token') @ApiOperation({ summary: 'confirm-email' }) - async confirmEmail(@Param('token') token: string, @Res() res: Response) { + @UseInterceptors(TokenCookieInterceptor) + async confirmEmail(@Param('token') token: string) { const { tokens, user } = await this.authService.confirmEmail(token); - const { access_token: accessToken, refresh_token: refreshToken } = tokens; - this.setTokens(res, accessToken, refreshToken); - - return res.status(HttpStatus.OK).send({ + return { status: 'success', message: 'Confirmed email successfully', user, - }); + ...tokens, + }; + } + + @Public() + @Sse('sse/:userId') + sse(@Param('userId') userId: string): Observable { + return this.sseService.getEventStream(null, userId); + } + + @Public() + @Sse('sse') + sseAll(): Observable { + return this.sseService.getEventStream(null, null); } @Patch('change-password') diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index f269d18..ee2a21f 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -6,9 +6,16 @@ import { JwtAtStrategy, JwtRtStrategy } from './strategy'; import { ConfigModule } from '@nestjs/config'; import { MailModule } from 'src/mail/mail.module'; import { UserModule } from 'src/user/user.module'; +import { SseModule } from 'src/sse/sse.module'; @Module({ - imports: [JwtModule.register({}), ConfigModule, MailModule, UserModule], + imports: [ + JwtModule.register({}), + ConfigModule, + MailModule, + UserModule, + SseModule, + ], controllers: [AuthController], providers: [AuthService, JwtRtStrategy, JwtAtStrategy], }) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 82f0cf5..a3fe140 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -3,18 +3,17 @@ import { ForbiddenException, Injectable, UnauthorizedException, - UseGuards, } from '@nestjs/common'; -import { SignInDto, SignUpDto } from './dto'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; -import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; -import { Tokens } from './types'; +import { JwtService } from '@nestjs/jwt'; +import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; import * as argon2 from 'argon2'; -import { JwtRtGuard } from './guard'; import { MailService } from 'src/mail/mail.service'; -import { JwtPayload } from './types/jwtPayloadType.type'; +import { SseService } from 'src/sse/sse.service'; import { UserRepository } from 'src/user/user.repository'; +import { SignInDto, SignUpDto } from './dto'; +import { Tokens } from './types'; +import { JwtPayload } from './types/jwtPayloadType.type'; import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; @Injectable() @@ -23,7 +22,8 @@ export class AuthService { private userRepository: UserRepository, private jwtService: JwtService, private configService: ConfigService, - private emailService: MailService + private emailService: MailService, + private sseService: SseService ) {} async signup(dto: SignUpDto) { @@ -41,11 +41,11 @@ export class AuthService { hashPasswordPromise, emailConfirmationTokenPromise, ]); - await this.userRepository.create({ + const user = await this.userRepository.create({ data: { + nickname: dto.nickname, email: dto.email, hash, - nickname: dto.nickname, emailConfirmationToken, }, }); @@ -53,6 +53,12 @@ export class AuthService { dto.email, emailConfirmationToken ); + return { + email: user.email, + id: user.id, + nickname: user.nickname, + isEmailConfirmed: user.isEmailConfirmed, + }; } catch (error) { if (error instanceof PrismaClientKnownRequestError) { if (error.code === 'P2002') { @@ -81,27 +87,17 @@ export class AuthService { throw new ForbiddenException('Password is incorrect'); } const tokens = await this.signTokens(user.id, user.email); - await this.updateRtHash(user.id, tokens.refresh_token); + await this.updateRtHash(user.id, tokens.refreshToken); return { tokens, - user: { nickname: user.nickname, email: user.email, id: user.id }, + user: { email: user.email, id: user.id }, }; } async me(user: JwtPayloadWithRt) { const userFromDB = await this.userRepository.findByEmail(user.email); if (!userFromDB) throw new UnauthorizedException('User does not exist'); - const access_token = await this.jwtService.signAsync( - { sub: userFromDB.id, email: userFromDB.email }, - { - expiresIn: '1d', - privateKey: this.configService.get('ACCESS_TOKEN_PRIV_KEY'), - algorithm: 'RS256', - } - ); return { - access_token, - nickname: userFromDB.nickname, email: userFromDB.email, id: userFromDB.id, isEmailConfirmed: userFromDB.isEmailConfirmed, @@ -121,18 +117,17 @@ export class AuthService { }, }); } - @UseGuards(JwtRtGuard) + async refreshTokens(id: string, rt: string) { const user = await this.userRepository.findById(id); - if (!user) throw new ForbiddenException('Access Denied'); if (!user.hashedRt) throw new ForbiddenException('Access Denied'); const rtMatches = await argon2.verify(user.hashedRt, rt); - if (!rtMatches) throw new ForbiddenException('Access Denied'); + if (!rtMatches) throw new ForbiddenException('Rt token does not match'); const tokens = await this.signTokens(user.id, user.email); - await this.updateRtHash(user.id, tokens.refresh_token); + await this.updateRtHash(user.id, tokens.refreshToken); return tokens; } @@ -158,8 +153,8 @@ export class AuthService { ]); return { - access_token: at_token, - refresh_token: rt_token, + accessToken: at_token, + refreshToken: rt_token, }; } @@ -178,8 +173,9 @@ export class AuthService { }); const tokens = await this.signTokens(user.id, user.email); - await this.updateRtHash(user.id, tokens.refresh_token); - return { tokens, user: { nickname: user.nickname, email: user.email } }; + await this.updateRtHash(user.id, tokens.refreshToken); + this.sseService.sendToUser(user.id, { type: 'email_verified' }); + return { tokens, user: { email: user.email } }; } async changePassword( @@ -268,7 +264,6 @@ export class AuthService { async updateRtHash(id: string, rt: string) { const hash = await argon2.hash(rt); - await this.userRepository.update({ where: { id, diff --git a/src/auth/guard/activeGame.guard.ts b/src/auth/guard/activeGame.guard.ts deleted file mode 100644 index 5b7815f..0000000 --- a/src/auth/guard/activeGame.guard.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CanActivate, Injectable } from '@nestjs/common'; -import { WsException } from '@nestjs/websockets'; -import { GameService } from 'src/game/game.service'; -import { parse } from 'cookie'; - -@Injectable() -export class ActiveGameGuard implements CanActivate { - constructor(private gameService: GameService) {} - - async canActivate(context: any): Promise { - const client = context.switchToWs().getClient(); - const { gameId } = parse(client.handshake.headers.cookie); - try { - const game = await this.gameService.getGame(gameId); - if (game.status !== 'ACTIVE') - throw new WsException('Game is not active anymore'); - client.game = game; - return true; - } catch (ex) { - throw new WsException(ex.message); - } - } -} diff --git a/src/auth/guard/hasLost.guard.ts b/src/auth/guard/hasLost.guard.ts deleted file mode 100644 index 9e1d832..0000000 --- a/src/auth/guard/hasLost.guard.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CanActivate, Injectable } from '@nestjs/common'; -import { WsException } from '@nestjs/websockets'; -import { PlayerService } from 'src/player/player.service'; - -@Injectable() -export class HasLostGuard implements CanActivate { - constructor(private playerService: PlayerService) {} - - async canActivate(context: any): Promise { - const client = context.switchToWs().getClient(); - const userId = client.jwtPayload.sub; - try { - const player = await this.playerService.findByUserAndGameId( - userId, - client.game.id - ); - if (player?.lost) throw new WsException('U have already lost'); - client.player = player; - return true; - } catch (ex) { - throw new WsException(ex.message); - } - } -} diff --git a/src/auth/guard/index.ts b/src/auth/guard/index.ts index 99057b5..b3a1b3b 100644 --- a/src/auth/guard/index.ts +++ b/src/auth/guard/index.ts @@ -1,5 +1,6 @@ export * from './jwt.guard'; export * from './jwt.refresh.guard'; export * from './role.guard'; -export * from './hasLost.guard'; +export * from './validPlayer.guard'; export * from './turn.guard'; +export * from './jwt.ws.guard'; diff --git a/src/auth/guard/jwt.ws.guard.ts b/src/auth/guard/jwt.ws.guard.ts index f7172a6..d29100b 100644 --- a/src/auth/guard/jwt.ws.guard.ts +++ b/src/auth/guard/jwt.ws.guard.ts @@ -14,18 +14,33 @@ export class WsGuard implements CanActivate { async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); if (!client.handshake.headers.cookie) { - throw new WsException('Unauthorized'); + throw new WsException({ + code: 'USER_NOT_AUTHENTICATED', + message: 'You must log in before performing this action', + details: { + action: 'WsGuard', + }, + }); } - - const { access_token } = parse(client.handshake.headers.cookie); + const userId = client.data.jwtPayload?.sub; + if (userId) { + return true; + } + const { accessToken } = parse(client.handshake.headers.cookie); try { - const decoded = await this.jwtService.verify(access_token, { + const decoded = await this.jwtService.verify(accessToken, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); - client.jwtPayload = decoded; + client.data.jwtPayload = decoded; return decoded; } catch (ex) { - throw new WsException(ex.message); + throw new WsException({ + code: 'USER_NOT_AUTHENTICATED', + message: ex.message, + details: { + action: 'WsGuard', + }, + }); } } } diff --git a/src/auth/guard/turn.guard.ts b/src/auth/guard/turn.guard.ts index e8ade90..4d8b2e1 100644 --- a/src/auth/guard/turn.guard.ts +++ b/src/auth/guard/turn.guard.ts @@ -1,14 +1,13 @@ import { CanActivate, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; -import { GameService } from 'src/game/game.service'; @Injectable() export class TurnGuard implements CanActivate { - constructor(private gameService: GameService) {} + constructor() {} async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); - const userId = client.jwtPayload.sub; + const userId = client.data.jwtPayload.sub; try { if ( client.game?.turnOfUserId !== userId && diff --git a/src/auth/guard/validPlayer.guard.ts b/src/auth/guard/validPlayer.guard.ts new file mode 100644 index 0000000..2eeb78f --- /dev/null +++ b/src/auth/guard/validPlayer.guard.ts @@ -0,0 +1,60 @@ +import { CanActivate, Injectable } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; +import { PlayerService } from 'src/player/player.service'; +import { parse } from 'cookie'; +import { JwtService } from '@nestjs/jwt'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class ValidPlayerGuard implements CanActivate { + constructor( + private playerService: PlayerService, + private jwtService: JwtService, + private configService: ConfigService + ) {} + + async canActivate(context: any): Promise { + const client = context.switchToWs().getClient(); + const userId = client.data.jwtPayload.sub; + if (!userId) { + if (!client.handshake.headers.cookie) { + throw new WsException('Unauthorized'); + } + const { accessToken } = parse(client.handshake.headers.cookie); + try { + const decoded = await this.jwtService.verify(accessToken, { + publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), + }); + client.data.jwtPayload = decoded; + } catch (ex) { + throw new WsException(ex.message); + } + } + let gameId = client.data.gameId; + if (!gameId) { + gameId = parse(client.handshake.headers.cookie).gameId; + } + if (!gameId) + throw new WsException({ + code: 'USER_NOT_IN_GAME', + message: 'You must join a game before performing this action', + details: { + action: 'ValidPlayerGuard', + }, + }); + try { + const player = await this.playerService.findByUserAndGameId( + userId || client.data.jwtPayload.sub, + gameId + ); + if (player?.game?.status !== 'ACTIVE') + throw new WsException('Game is not active anymore'); + if (player?.lost) throw new WsException('You have already lost'); + client.player = player; + client.game = player.game; + return true; + } catch (ex) { + throw new WsException(ex.message); + } + } +} diff --git a/src/auth/interceptor/authCookies.interceptor.ts b/src/auth/interceptor/authCookies.interceptor.ts new file mode 100644 index 0000000..99caa97 --- /dev/null +++ b/src/auth/interceptor/authCookies.interceptor.ts @@ -0,0 +1,49 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Response } from 'express'; + +interface AuthResponse { + accessToken?: string; + refreshToken?: string; + [key: string]: any; +} + +@Injectable() +export class TokenCookieInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + map((data: AuthResponse) => { + const response = context.switchToHttp().getResponse(); + if (data?.accessToken || data?.refreshToken) { + if (data.accessToken) { + response.cookie('accessToken', data.accessToken, { + httpOnly: true, + maxAge: 1000 * 60 * 15, + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + secure: process.env.NODE_ENV === 'production', + }); + delete data.accessToken; + } + + if (data.refreshToken) { + response.cookie('refreshToken', data.refreshToken, { + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7, + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + secure: process.env.NODE_ENV === 'production', + }); + delete data.refreshToken; + } + } + + return data; + }), + ); + } +} diff --git a/src/auth/strategy/jwt.at.strategy.ts b/src/auth/strategy/jwt.at.strategy.ts index b55a737..17f1825 100644 --- a/src/auth/strategy/jwt.at.strategy.ts +++ b/src/auth/strategy/jwt.at.strategy.ts @@ -11,7 +11,7 @@ export class JwtAtStrategy extends PassportStrategy(Strategy, 'jwt') { super({ jwtFromRequest: ExtractJwt.fromExtractors([ (request: Request) => { - const token = request.cookies['access_token']; + const token = request.cookies['accessToken']; return token; }, ]), diff --git a/src/auth/strategy/jwt.rt.strategy.ts b/src/auth/strategy/jwt.rt.strategy.ts index 979439a..b135750 100644 --- a/src/auth/strategy/jwt.rt.strategy.ts +++ b/src/auth/strategy/jwt.rt.strategy.ts @@ -12,7 +12,7 @@ export class JwtRtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') { super({ jwtFromRequest: ExtractJwt.fromExtractors([ (request: Request) => { - const token = request.cookies['refresh_token']; + const token = request.cookies['refreshToken']; return token; }, ]), @@ -22,7 +22,7 @@ export class JwtRtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') { }); } validate(req: Request, payload: JwtPayload): JwtPayloadWithRt { - const refreshToken = req.cookies['refresh_token']; + const refreshToken = req.cookies['refreshToken']; return { ...payload, refreshToken, diff --git a/src/auth/types/cookiesAttributes.type.ts b/src/auth/types/cookiesAttributes.type.ts deleted file mode 100644 index 03f9197..0000000 --- a/src/auth/types/cookiesAttributes.type.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type AccessCookieAttributes = { - httpOnly: boolean; - sameSite: 'lax' | 'strict' | 'none'; - secure?: boolean; - domain: string; -}; - -export type RefreshCookieAttributes = { - httpOnly: boolean; - secure?: boolean; - sameSite: 'lax' | 'strict' | 'none'; - path: string; - domain: string; -}; diff --git a/src/auth/types/index.ts b/src/auth/types/index.ts index ff69d7f..45bba05 100644 --- a/src/auth/types/index.ts +++ b/src/auth/types/index.ts @@ -1,2 +1 @@ export * from './tokens.types'; -export * from './cookiesAttributes.type'; diff --git a/src/auth/types/tokens.types.ts b/src/auth/types/tokens.types.ts index 1c0a510..bef8723 100644 --- a/src/auth/types/tokens.types.ts +++ b/src/auth/types/tokens.types.ts @@ -1,4 +1,4 @@ export type Tokens = { - access_token: string; - refresh_token: string; + accessToken: string; + refreshToken: string; }; diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 36410bd..4df3410 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -42,36 +42,41 @@ export class ChatGateway implements OnModuleInit { @SubscribeMessage('newMessage') async onNewMessage( socket: Socket & { jwtPayload: JwtPayload }, - data: NewMessagePayloadDto + data: { newMessage: NewMessagePayloadDto; requestId: string } ) { const message = await this.chatService.onNewMessage( - socket.jwtPayload.sub, - data + socket.data.jwtPayload.sub, + data.newMessage ); - this.server.emit('onMessage', message); + this.server.emit('onMessage', { ...message, requestId: data.requestId }); } + @UseGuards(WsGuard) @SubscribeMessage('newGameMessage') async onNewGameMessage( socket: Socket & { jwtPayload: JwtPayload }, - data: NewGameMessageDto + data: { newMessage: NewGameMessageDto; requestId: string } ) { const message = await this.chatService.onNewMessage( - socket.jwtPayload.sub, - data + socket.data.jwtPayload.sub, + data.newMessage ); - if (Array.from(socket.rooms).includes(data.gameId)) - this.server.to(data.gameId).emit('gameChatMessage', message); + if (Array.from(socket.rooms).includes(data.newMessage.gameId)) + this.server + .to(data.newMessage.gameId) + .emit('gameChatMessage', { ...message, requestId: data.requestId }); } @SubscribeMessage('chatData') - onChatData(@MessageBody() data: { chatId: string }) { - return this.chatService.onChatData(data.chatId); + async onChatData(@MessageBody() data: { chatId: string }) { + const chat = await this.chatService.onChatData(data.chatId); + return chat; } @SubscribeMessage('mutualChatData') - onMutualChatData() { - return this.chatService.onMutualChatData(); + async onMutualChatData() { + const mutualChat = await this.chatService.onMutualChatData(); + return mutualChat; } } diff --git a/src/common/IHandler.ts b/src/common/IHandler.ts new file mode 100644 index 0000000..933fbd2 --- /dev/null +++ b/src/common/IHandler.ts @@ -0,0 +1,4 @@ +export interface IHandler { + canHandle(): boolean | void; + handle(): void; +} diff --git a/src/common/base.handler.ts b/src/common/base.handler.ts new file mode 100644 index 0000000..bc09763 --- /dev/null +++ b/src/common/base.handler.ts @@ -0,0 +1,15 @@ +import { IHandler } from './IHandler'; + +export abstract class BaseHandler implements IHandler { + constructor( + protected analyzer: T, + private handler?: () => void + ) {} + + abstract canHandle(): boolean | void; + handle() { + if (this.handler) { + this.handler(); + } + } +} diff --git a/src/common/handlerChain.ts b/src/common/handlerChain.ts new file mode 100644 index 0000000..fcf2df2 --- /dev/null +++ b/src/common/handlerChain.ts @@ -0,0 +1,17 @@ +import { IHandler } from './IHandler'; + +export class HandlerChain { + private handlers: IHandler[] = []; + addHandlers(...handlers: IHandler[]): void { + this.handlers.push(...handlers); + } + + process(): unknown | Promise { + for (const handler of this.handlers) { + if (handler.canHandle()) { + return handler.handle(); + } + } + console.log('No handler found for this data'); + } +} diff --git a/src/event/event.module.ts b/src/event/event.module.ts deleted file mode 100644 index 4880832..0000000 --- a/src/event/event.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EventEmitterModule } from '@nestjs/event-emitter'; -import { EventService } from './event.service'; - -@Module({ - imports: [EventEmitterModule.forRoot()], - providers: [EventService], - exports: [EventService], -}) -export class EventModule {} diff --git a/src/event/event.service.ts b/src/event/event.service.ts deleted file mode 100644 index d98c85e..0000000 --- a/src/event/event.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { EventEmitter2 } from '@nestjs/event-emitter'; - -@Injectable() -export class EventService { - constructor(private eventEmitter: EventEmitter2) {} - - emitGameEvent(eventName: string, data: any) { - this.eventEmitter.emit(eventName, data); - } -} diff --git a/src/field/FieldAnalyzer.ts b/src/field/FieldAnalyzer.ts new file mode 100644 index 0000000..a9f8e09 --- /dev/null +++ b/src/field/FieldAnalyzer.ts @@ -0,0 +1,47 @@ +import { GamePayload } from 'src/game/game.repository'; +import { PlayerPayload } from 'src/player/player.repository'; +import { PlayerService } from 'src/player/player.service'; +import { FieldDocument } from 'src/schema/Field.schema'; + +export class FieldAnalyzer { + readonly currentPlayer: Partial; + + constructor( + readonly field: FieldDocument, + readonly game: Partial, + private playerService: PlayerService + ) { + this.currentPlayer = this.playerService.findPlayerWithTurn(game); + } + isOwnedByCurrentUser(): boolean { + return ( + this.field.ownedBy === this.currentPlayer.userId && this.field.price > 0 + ); + } + isOwnedByOtherAndNotPledged(): boolean { + return ( + this.field.ownedBy && + this.field.ownedBy !== this.currentPlayer.userId && + !this.field.isPledged + ); + } + isNotOwned(): boolean { + return !this.field.ownedBy && this.field.price > 0; + } + isAffordableForSomeone(): boolean { + return this.game.players.some( + (player) => + player.userId !== this.currentPlayer.userId && + player.money > this.field.price + ); + } + isSpecialField(): boolean { + return !this.field.price; + } + isSkipable(): boolean { + return ( + (this.field?.specialField && !this.field.secret && !this.field.toPay) || + this.field.isPledged + ); + } +} diff --git a/src/field/field.module.ts b/src/field/field.module.ts new file mode 100644 index 0000000..3e971d8 --- /dev/null +++ b/src/field/field.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common'; +import { FieldService } from './field.service'; +import { MongooseModule } from '@nestjs/mongoose'; +import { Field, FieldSchema } from 'src/schema/Field.schema'; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { + name: Field.name, + schema: FieldSchema, + }, + ]), + ], + providers: [FieldService], + exports: [ + FieldService, + MongooseModule.forFeature([{ name: Field.name, schema: FieldSchema }]), + ], +}) +export class FieldModule {} diff --git a/src/field/field.service.ts b/src/field/field.service.ts new file mode 100644 index 0000000..d6b3ade --- /dev/null +++ b/src/field/field.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { Field, FieldDocument } from 'src/schema/Field.schema'; +import { FieldType } from 'src/utils/fields'; + +@Injectable() +export class FieldService { + constructor( + @InjectModel(Field.name) + private fieldModel: Model + ) {} + async getGameFields(gameId: string) { + return await this.fieldModel.find({ gameId }); + } + + createMany(gameFields: FieldType[]) { + return this.fieldModel.insertMany(gameFields); + } + + updateFields( + fields: T[], + propertiesToUpdate: string[] + ) { + const updates = fields.map((field) => { + const updateFields: any = {}; + + for (const property of propertiesToUpdate) { + if (field[property] !== undefined) { + updateFields[property] = field[property]; + } + } + + return { + updateOne: { + filter: { _id: field._id }, + update: { $set: updateFields }, + }, + }; + }); + return this.fieldModel.bulkWrite(updates); + } + + findPlayerFieldByIndex(fields: FieldDocument[], indexOfField: number) { + return fields.find((field) => field.index === indexOfField); + } + + async updateById(id: unknown, field: Partial) { + return await this.fieldModel.updateOne({ _id: id }, { $set: field }); + } +} diff --git a/src/game/decorators/getGameCookieWs.ts b/src/game/decorators/getGameCookieWs.ts index e745858..0e661a3 100644 --- a/src/game/decorators/getGameCookieWs.ts +++ b/src/game/decorators/getGameCookieWs.ts @@ -6,7 +6,14 @@ export const GetGameId = createParamDecorator( (_: undefined, context: ExecutionContext): string => { const client = context.switchToWs().getClient(); const { gameId } = parse(client.handshake.headers.cookie); - if (!gameId) throw new WsException('You are not playing'); + if (!gameId) + throw new WsException({ + code: 'USER_NOT_IN_GAME', + message: 'You must join a game before performing this action', + details: { + action: 'GetGameId', + }, + }); return gameId; } ); diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 7b4fb48..2d709e0 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -1,6 +1,5 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { OnEvent } from '@nestjs/event-emitter'; import { JwtService } from '@nestjs/jwt'; import { ConnectedSocket, @@ -8,28 +7,19 @@ import { SubscribeMessage, WebSocketGateway, WebSocketServer, - WsException, } from '@nestjs/websockets'; import { parse } from 'cookie'; import { Server, Socket } from 'socket.io'; -import { HasLostGuard } from 'src/auth/guard'; -import { ActiveGameGuard } from 'src/auth/guard/activeGame.guard'; -import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; -import { TurnGuard } from 'src/auth/guard/turn.guard'; +import { AuctionService } from 'src/auction/auction.service'; +import { TurnGuard, ValidPlayerGuard, WsGuard } from 'src/auth/guard'; import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; -import { PlayerPayload } from 'src/player/player.repository'; -import { PlayerService } from 'src/player/player.service'; +import { TimerService } from 'src/timer/timers.service'; import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; -import { WebSocketServerService } from 'src/webSocketServer/webSocketServer.service'; -import { ChatService } from './../chat/chat.service'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; import { GetGameId } from './decorators/getGameCookieWs'; import { GamePayload } from './game.repository'; import { GameService } from './game.service'; -import { Auction } from './types/auction.type'; -import { Trade } from './types/trade.type'; -import { FieldDocument } from 'src/schema/Field.schema'; -import { AuctionService } from 'src/auction/auction.service'; @WebSocketGateway({ cors: { @@ -43,45 +33,33 @@ import { AuctionService } from 'src/auction/auction.service'; }) @UseFilters(WebsocketExceptionsFilter) @UsePipes(new WsValidationPipe()) -@UseGuards(WsGuard) export class GameGateway { constructor( private gameService: GameService, - private playerService: PlayerService, + private webSocketProvider: WebSocketProvider, private jwtService: JwtService, private configService: ConfigService, - private chatService: ChatService, - private webSocketServer: WebSocketServerService, - private auctionService: AuctionService - ) { - this.rollDice = this.rollDice.bind(this); - this.putUpForAuction = this.putUpForAuction.bind(this); - this.passTurnToNext = this.passTurnToNext.bind(this); - this.winAuction = this.winAuction.bind(this); - this.payForField = this.payForField.bind(this); - this.payToBank = this.payToBank.bind(this); - this.payAll = this.payAll.bind(this); - this.resolveTwoUsers = this.resolveTwoUsers.bind(this); - } - + private auctionService: AuctionService, + private timerService: TimerService + ) {} @WebSocketServer() - private server: Server; - + readonly server: Server; afterInit(server: Server) { - this.webSocketServer.setServer(server); + this.webSocketProvider.setServer(server); } - - async handleConnection(socket: Socket & { jwtPayload: JwtPayload }) { + async handleConnection( + socket: Socket & { jwtPayload: JwtPayload; gameId: string } + ) { try { - const cookies = await this.extractCookies(socket); - if (!cookies) return; - const { gameId, userId } = cookies; - if (!gameId) return; + await this.extractCookies(socket); + const userId = socket.data.jwtPayload?.sub; + const gameId = socket.gameId; if (!userId) return; + if (!gameId) return; socket.join(userId); const game = await this.gameService.getGame(gameId); - if (!game || game.status !== 'ACTIVE') return; - const timers = this.gameService.timers; + if (game.status !== 'ACTIVE') return; + const timers = this.timerService.timers; if (timers.has(gameId)) { this.rejoinGame(socket, gameId); return; @@ -90,38 +68,35 @@ export class GameGateway { this.rejoinGame(socket, gameId); const currentField = await this.gameService.findCurrentFieldFromGame(game); - let timerCallback: (args: unknown) => Promise; if (game.dices) { - timerCallback = currentField?.price - ? this.putUpForAuction - : this.passTurnToNext; + this.gameService.processRolledDices(game, currentField); } else { - timerCallback = this.rollDice; + this.timerService.set( + gameId, + updatedGame.timeOfTurn, + updatedGame, + this.gameService.rollDice + ); } - - this.gameService.setTimer( - gameId, - updatedGame.timeOfTurn, - updatedGame, - timerCallback - ); } catch (err) { console.log(err); } } - private async extractCookies(socket: Socket & { jwtPayload: JwtPayload }) { + private async extractCookies(socket: Socket) { const cookies = socket.handshake.headers.cookie ? parse(socket.handshake.headers.cookie) : null; if (!cookies?.gameId) return null; - const { gameId, access_token } = cookies; - const decoded = await this.jwtService.verify(access_token, { + const { gameId, accessToken } = cookies; + const decoded = await this.jwtService.verify(accessToken, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); - return { gameId, userId: decoded.sub }; + socket['jwtPayload'] = decoded; + socket['gameId'] = gameId; + socket.data.jwtPayload = decoded; } private rejoinGame(socket: Socket, gameId: string) { @@ -141,803 +116,301 @@ export class GameGateway { } @SubscribeMessage('getVisibleGames') - async getVisibleGames() { + async onGetVisibleGames() { const games = await this.gameService.getVisibleGames(); return games; } - @SubscribeMessage('getGameData') - async getGameData(@GetGameId() gameId: string) { - const game = await this.gameService.getGame(gameId); - const auction = this.auctionService.auctions.get(game.id); - const secretInfo = this.gameService.secrets.get(game.id); - const fields = await this.gameService.getGameFields(game.id); - this.server.emit('gameData', { game, fields, auction, secretInfo }); + @SubscribeMessage('getAllGameData') + async onGetAllGameData( + @GetGameId() gameId: string, + @MessageBody() data: { requestId: string } + ) { + const { game, fields, auction, secretInfo } = + await this.gameService.getAllGameData(gameId, data.requestId); + this.server.to(gameId).emit('gameData', { + game, + fields, + auction, + secretInfo, + requestId: data.requestId, + }); + } + + @UseGuards(WsGuard) + @SubscribeMessage('createGame') + async createGame( + @ConnectedSocket() socket: Socket, + @MessageBody() data: { requestId: string } + ) { + const createdGameWithPlayer = await this.gameService.createGame( + socket.data.jwtPayload.sub, + data.requestId + ); + socket.join(createdGameWithPlayer.id); + return this.server.emit('newGameCreated', { + game: createdGameWithPlayer, + requestId: data.requestId, + }); } + @UseGuards(WsGuard) @SubscribeMessage('joinGame') async onJoinGame( - socket: Socket & { jwtPayload: JwtPayload }, - data: { id: string } + @ConnectedSocket() socket: Socket, + @MessageBody() data: { id: string; requestId: string } ) { - const { game, shouldStart } = await this.gameService.onJoinGame( + console.log({ data }); + const game = await this.gameService.joinGame( data.id, - socket.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); - if (game) { - this.leaveAllRoomsExceptInitial(socket); - socket.join(data.id); - this.server.emit('onParticipateGame', game); - if (shouldStart) { - // We can setTimeout here for some countdown on frontend - this.server.emit('clearStartedGame', { - gameId: game.id, - }); - this.server.to(game.id).emit('startGame', { - game, - chatId: game.chat.id, - }); - this.gameService.setTimer( - game.id, - game.timeOfTurn, - game, - this.rollDice - ); - } + this.leaveAllRoomsExceptInitial(socket); + socket.join(data.id); + if (game.status === 'ACTIVE') { + this.gameService.startGame(game); } + + this.server.emit('onParticipateGame', { + game, + requestId: data.requestId, + }); } + @UseGuards(WsGuard) @SubscribeMessage('leaveGame') async onLeaveGame( - socket: Socket & { jwtPayload: JwtPayload }, - data: { id: string } + socket: Socket & { jwtPayload: JwtPayload; user: JwtPayload }, + data: { id: string; requestId: string } ) { - const game = await this.gameService.onLeaveGame( + const game = await this.gameService.leaveGame( data.id, - socket.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); if (game) { this.leaveAllRoomsExceptInitial(socket); this.server.emit('onParticipateGame', { - id: data.id, - players: game.players, + game, + requestId: data.requestId, }); } } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('rollDice') async onRollDice( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - if (socket.game.dices) - throw new WsException('You have already rolled dices'); - this.gameService.clearTimer(socket.game.id); - await this.rollDice(socket.game); + await this.gameService.rollDice(socket.game, data.requestId); } - async rollDice(game: Partial) { - const { - updatedGame, - nextIndex, - playerNextField, - hasOwner, - currentPlayer, - fields, - } = await this.gameService.makeTurn(game); - if ( - playerNextField.price && - playerNextField.ownedBy === updatedGame?.turnOfUserId - ) { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); - } - this.server.to(game.id).emit('rolledDice', { - fields, - playerNextField, - hasOwner, - moveToIndex: nextIndex, - game: updatedGame, - }); - if ( - hasOwner && - playerNextField.ownedBy !== currentPlayer.userId && - !playerNextField.isPledged - ) { - this.steppedOnPrivateField(currentPlayer, playerNextField, updatedGame); - return; - } - if (playerNextField.price && !playerNextField.ownedBy) { - if ( - updatedGame.players.some( - (player) => player.money > playerNextField.price - ) - ) { - this.gameService.setTimer( - game.id, - game.timeOfTurn, - updatedGame, - this.putUpForAuction - ); - } else { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); - } - } - - if (!playerNextField.price) { - // this.gameService.setTimer( - // game.id, - // game.timeOfTurn, - // updatedGame, - // this.passTurnToNext - // ); - this.processSpecialField(updatedGame, playerNextField); - } - const currentField = - await this.gameService.findCurrentFieldWithUserId(updatedGame); - if ( - currentField?.specialField && - !currentField.secret && - !currentField.toPay - ) { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); - } - - if (playerNextField.isPledged) { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); - } - } - - async processSpecialField( - game: Partial, - playerNextField: FieldDocument + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('payToBankForSpecialField') + async onPayToBankForSpecialField( + @ConnectedSocket() + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - if (playerNextField.toPay) { - this.gameService.setTimer( - game.id, - game.timeOfTurn, - { game, userId: game.turnOfUserId, amount: playerNextField.toPay }, - this.payToBank - ); - } - if (playerNextField.secret) { - const secret = this.gameService.getRandomSecret(); - const secretInfo = await this.gameService.parseAndSaveSecret( - secret, - game - ); - const randomPlayer = game.players.find( - (player) => player.userId === secretInfo.users[1] - ); - if (secret.text.includes('$RANDOM_PLAYER$')) { - secret.text = secret.text.replace( - '$RANDOM_PLAYER$', - randomPlayer?.user.nickname - ); - } - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: secret.text, - chatId: game.chat.id, - }); - this.server.to(game.id).emit('gameChatMessage', message); - this.server.to(game.id).emit('secret', secretInfo); - secret.text = secret.text.replace( - randomPlayer?.user.nickname, - '$RANDOM_PLAYER$' - ); - if (secretInfo.users.length === 1) { - if (secretInfo.amounts[0] < 0) { - this.gameService.setTimer( - game.id, - game.timeOfTurn, - { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, - this.payToBank - ); - } else { - this.gameService.setTimer( - game.id, - 3500, - { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, - this.payToBank - ); - } - } else if (secretInfo.users.length === 2) { - this.gameService.setTimer( - game.id, - game.timeOfTurn, - game, - this.resolveTwoUsers - ); - } else if (secretInfo.users.length > 2) { - this.gameService.setTimer(game.id, game.timeOfTurn, game, this.payAll); - } - } - } - - async payAll(game: Partial) { - const secretInfo = this.gameService.secrets.get(game.id); - let updatedPlayer = null; - for (const userId of secretInfo.users) { - const firstUser = secretInfo.users[0]; - if (userId && userId !== firstUser) { - if (secretInfo.amounts.length === 2) { - updatedPlayer = await this.payToUser({ - game, - userId, - userToPayId: firstUser, - amount: secretInfo.amounts[1], - }); - } - - if (secretInfo.amounts.length === 1) { - const { playerWhoPayed } = await this.gameService.payToBank( - game, - userId, - secretInfo.amounts[0] - ); - updatedPlayer = playerWhoPayed; - } - } - } - this.gameService.secrets.delete(game.id); - this.passTurnToNext(updatedPlayer?.game || game); - this.server.to(game.id).emit('updatePlayers', { - game: updatedPlayer?.game, - }); - } - - async resolveTwoUsers(game: Partial) { - let secretInfo = this.gameService.secrets.get(game.id); - const firstPay = secretInfo.amounts[0] < 1; - let updatedGameToReturn: null | Partial = null; - const fields = await this.gameService.getGameFields(game.id); - if (firstPay) { - const userId = secretInfo.users[0]; - if (userId) { - const player = game.players.find((player) => player.userId === userId); - if ( - this.playerService.estimateAssets(player, fields) < - secretInfo.amounts[0] - ) { - await this.playerService.loseGame(player.userId, game.id, fields); - return; - } - const { updatedGame } = await this.gameService.payToBank( - game, - userId, - secretInfo.amounts[0] - ); - updatedGameToReturn = updatedGame; - } - if (secretInfo.users[1]) { - const { updatedGame } = await this.gameService.payToBank( - game, - secretInfo.users[1], - secretInfo.amounts[1] - ); - updatedGameToReturn = updatedGame; - } - } else { - const userId = secretInfo.users[1]; - if (userId) { - const player = game.players.find((player) => player.userId === userId); - if ( - this.playerService.estimateAssets(player, fields) < - secretInfo.amounts[1] - ) { - await this.playerService.loseGame(player.userId, game.id, fields); - return; - } - const { updatedGame } = await this.gameService.payToBank( - game, - userId, - secretInfo.amounts[1] - ); - updatedGameToReturn = updatedGame; - } - if (secretInfo.users[0]) { - const { updatedGame } = await this.gameService.payToBank( - game, - secretInfo.users[0], - secretInfo.amounts[0] - ); - updatedGameToReturn = updatedGame; - } - } - secretInfo = null; - this.passTurnToNext(updatedGameToReturn); + await this.gameService.payToBankForSpecialField( + socket.data.jwtPayload.sub, + socket.game, + data.requestId + ); } - @UseGuards(ActiveGameGuard, HasLostGuard) - @SubscribeMessage('payToUser') - async onPayToUser( + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('payToUserForSecret') + async onPayToUserForSecret( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { const game = socket.game; - const userId = socket.jwtPayload.sub; - const secretInfo = this.gameService.secrets.get(game.id); - return this.payToUser({ - game, - userId, - userToPayId: secretInfo.users[0], - amount: secretInfo.amounts[1], - }); - } - - async payToUser({ - game, - userId, - userToPayId, - amount, - }: { - game: Partial; - userId: string; - userToPayId: string; - amount: number; - }) { - let secretInfo = this.gameService.secrets.get(game.id); - if (!secretInfo.users.includes(userId)) - throw new WsException('You cant pay for that secret'); - const indexOfUser = secretInfo.users.indexOf(userId); - if (amount > 0) - throw new WsException('You dont have to pay for this secret field'); - const player = game.players.find((player) => player.userId === userId); - const fields = await this.gameService.getGameFields(game.id); - let updatedPlayer = null; - if (player.money < amount) { - const userToPay = game.players.find( - (player) => player.userId === userToPayId - ); - updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( - userToPayId, - game.id, - this.playerService.estimateAssets(userToPay, fields) - ); - await this.playerService.loseGame(player.userId, game.id, fields); - } else { - await this.playerService.incrementMoneyWithUserAndGameId( - userId, - game.id, - amount - ); - updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( - userToPayId, - game.id, - -amount - ); - } - secretInfo.users.splice(indexOfUser, 1, ''); - if ( - secretInfo.users.every((userId, index) => { - if (secretInfo.amounts[index] > 0) return true; - return userId === ''; - }) - ) { - secretInfo = null; - } + const userId = socket.data.jwtPayload.sub; + const { game: updatedGame, secretInfo } = + await this.gameService.payToUserForSecret(game, userId, data.requestId); this.server.to(game.id).emit('updatePlayers', { - game: updatedPlayer.game, + game: updatedGame, secretInfo, + requestId: data.requestId, }); - return updatedPlayer; } - @UseGuards(ActiveGameGuard, HasLostGuard) - @SubscribeMessage('payToBank') - async onPayToBank( + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('payToBankForSecret') + async onPayToBankForSecret( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - const currentField = await this.gameService.findCurrentFieldWithUserId( - socket.game - ); - const secretInfo = this.gameService.secrets.get(socket.game.id); - if (!socket.game.dices && !currentField.toPay && !secretInfo) - throw new WsException( - 'You cant pay for that field because smt is missing' - ); - let amountToPay = 0; - if (secretInfo) { - if (!secretInfo.users.includes(socket.jwtPayload.sub)) { - throw new WsException( - 'You cant pay to bank because no user in secretInfo' - ); - } - if (secretInfo.users.length === 1) { - if (secretInfo.amounts[0] > 0) { - throw new WsException( - 'You cant pay to bank because one user and he doesnt have to pay' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } else if (secretInfo.users.length === 2) { - const index = secretInfo.users.findIndex( - (userId) => userId === socket.jwtPayload.sub - ); - if (secretInfo.amounts[index] > 0) { - throw new WsException( - 'You cant pay to bank two users and the one wants to pay dont have to' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } else if ( - secretInfo.users.length > 2 && - socket.jwtPayload.sub !== secretInfo.users[0] - ) { - if (secretInfo.amounts.length === 2) { - if (secretInfo.amounts[1] > 0) { - throw new WsException( - 'You cant pay to bank more then 2 and this doesnt lya lya' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } - - if (secretInfo.amounts.length === 1) { - if (secretInfo.amounts[0] > 0) { - throw new WsException( - 'You cant pay to bank more than 2 one amount and dont have t' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } - if ( - secretInfo.users.every((userId, index) => { - if (secretInfo.amounts[index] > 0) return true; - return userId === ''; - }) - ) { - throw new WsException( - 'You cant pay to bank every user is empty string' - ); - } - } - } - if (currentField.toPay) { - this.payToBank({ - game: socket.game, - userId: socket.jwtPayload.sub, - amount: currentField.toPay, - }); - } - if (secretInfo) { - this.payToBank({ - game: socket.game, - userId: socket.jwtPayload.sub, - amount: amountToPay, - }); - const indexOfUser = secretInfo.users.findIndex( - (userId) => userId === socket.jwtPayload.sub - ); - secretInfo.users[indexOfUser] = ''; - } - } - - async payToBank(argsObj: { - game: Partial; - amount: number; - userId: string; - }) { - const { updatedGame } = await this.gameService.payToBank( - argsObj.game, - argsObj.userId, - argsObj.amount - ); - const secretInfo = this.gameService.secrets.get(updatedGame.id); - if (!secretInfo) { - await this.passTurnToNext(updatedGame); - } else { - this.server.to(updatedGame.id).emit('updatePlayers', { - game: updatedGame, - secretInfo, - }); - } - } - - async steppedOnPrivateField( - player: Partial, - field: FieldDocument, - game: Partial - ) { - const fields = await this.gameService.getGameFields(game.id); - if ( - this.playerService.estimateAssets(player, fields) >= - field.income[field.amountOfBranches] - ) { - this.gameService.setTimer( - game.id, - game.timeOfTurn, - { game, field: field }, - this.payForField - ); - return; - } - const { updatedPlayer } = await this.playerService.loseGame( - player.userId, - game.id, - fields - ); - - await this.passTurnToNext(updatedPlayer.game); + const game = socket.game; + const userId = socket.data.jwtPayload.sub; + await this.gameService.payToBankForSecret(game, userId, data.requestId); } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) - @SubscribeMessage('payForField') - async onPayForField( + @UseGuards(ValidPlayerGuard, TurnGuard) + @SubscribeMessage('payForPrivateField') + async onPayForPrivateField( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - const currentField = await this.gameService.findCurrentFieldWithUserId( - socket.game - ); - if (!socket.game.dices || !currentField.ownedBy) - throw new WsException('You cant pay for that field'); - this.gameService.clearTimer(socket.game.id); - await this.payForField({ game: socket.game, field: currentField }); + await this.gameService.payForPrivateField(socket.game, data.requestId); } - async payForField({ - game, - field, - }: { - game: Partial; - field: FieldDocument; - }) { - const { updatedGame, fields: updatedFields } = - await this.gameService.payForField(game, field); - this.server.to(game.id).emit('payedForField', { - game: updatedGame, - fields: updatedFields, - }); - this.passTurnToNext(updatedGame); - } - - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('putUpForAuction') async onPutUpForAuction( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.putUpForAuction(socket.game); + await this.gameService.putUpForAuction(socket.game, data.requestId); } - async putUpForAuction(game: Partial) { - const auction = await this.auctionService.putUpForAuction(game); - - const updatedGame = await this.gameService.updateGameWithNewTurn( - game, - 15000 - ); - this.server - .to(game.id) - .emit('hasPutUpForAuction', { game: updatedGame, auction }); - this.gameService.setTimer(game.id, 15000, updatedGame, this.passTurnToNext); - } - - @SubscribeMessage('createGame') - async createGame(socket: Socket & { jwtPayload: JwtPayload }) { - const createdGameWithPlayer = await this.gameService.createGame( - socket.jwtPayload.sub - ); - - if (!createdGameWithPlayer) { - return socket.emit('error', { - message: - 'Ви вже знаходитесь в кімнаті, покиньте її щоб приєднатись до іншої', - }); - } else { - socket.join(createdGameWithPlayer.id); - } - - return this.server.emit('newGameCreated', createdGameWithPlayer); - } - - @UseGuards(ActiveGameGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard) @SubscribeMessage('raisePrice') async raisePrice( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, @GetGameId() gameId: string, - @MessageBody('raiseBy') raiseBy: number, - @MessageBody('bidAmount') bidAmount: number + @MessageBody() + data: { raiseBy: number; bidAmount: number; requestId: string } ) { - const userId = socket.jwtPayload.sub; - const auction = await this.auctionService.raisePrice( + console.log({ data }); + const userId = socket.data.jwtPayload.sub; + await this.auctionService.raisePrice( gameId, userId, - raiseBy, - bidAmount + data.raiseBy, + data.bidAmount, + data.requestId ); - - if (auction) { - this.server.to(gameId).emit('raisedPrice', { auction }); - this.gameService.setTimer( - gameId, - 15000, - { ...auction, gameId }, - this.winAuction - ); - } else { - this.server.to(userId).emit('error', { - message: 'Хтось одночасно поставив з вами і перебив вашу ставку', - }); - } } - @UseGuards(ActiveGameGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard) @SubscribeMessage('refuseAuction') async refuseAuction( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, - @GetGameId() gameId: string + @GetGameId() gameId: string, + @MessageBody() data: { requestId: string } ) { - const userId = socket.jwtPayload.sub; - const { auction, hasWinner, finished, game } = - await this.auctionService.refuseAuction(gameId, userId); - if (finished) { - if (hasWinner) { - this.winAuction({ ...auction, gameId }); - } else { - this.passTurnToNext(game); - } - } else { - this.server.to(gameId).emit('refusedFromAuction', { auction }); - } + const userId = socket.data.jwtPayload.sub; + await this.auctionService.refuseAuction(gameId, userId, data.requestId); } - async winAuction(auction: Auction & { gameId: string }) { - const { updatedPlayer, fields } = - await this.auctionService.winAuction(auction); - this.server - .to(auction.gameId) - .emit('wonAuction', { auction, game: updatedPlayer.game, fields }); - const game = await this.gameService.getGame(auction.gameId); - this.passTurnToNext(game); - } - - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('buyField') async onBuyField( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.buyField(socket.game); - } - - async buyField(game: Partial) { - await this.gameService.buyField(game); - this.passTurnToNext(game); + await this.gameService.buyField(socket.game, data.requestId); } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('passTurn') async onPassTurn( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - const fields = await this.gameService.getGameFields(socket.game.id); - const currentPlayer = this.playerService.findPlayerWithTurn(socket.game); - const currentField = this.gameService.findPlayerFieldByIndex( - fields, - currentPlayer.currentFieldIndex - ); - if (!currentField.large && currentField.ownedBy !== socket.jwtPayload.sub) { - throw new WsException('You cant pass turn with that field'); - } - await this.passTurnToNext(socket.game); + await this.gameService.passTurn(socket.game, data.requestId); } - async passTurnToNext(game: Partial) { - const { updatedGame } = await this.gameService.passTurnToNext(game); - const fields = await this.gameService.getGameFields(game.id); - this.server - .to(game.id) - .emit('passTurnToNext', { game: updatedGame, fields }); - this.gameService.setTimer( - game.id, - game.timeOfTurn, - updatedGame, - this.rollDice + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('buyBranch') + async onBuyBranch( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { index: number; requestId: string } + ) { + const index = data.index; + const game = socket.game; + const userId = socket.data.jwtPayload.sub; + const { updatedGame, fields } = await this.gameService.buyBranch( + game, + index, + userId, + data.requestId ); - } - @OnEvent('passTurnToNext') - async handlePassTurnToNext(data: { game: Partial }) { - this.passTurnToNext(data.game); - } - @OnEvent('offerTrade') - async handleOfferTrade(data: { game: Partial; trade: Trade }) { - const { updatedGame } = await this.gameService.passTurnToUser({ - game: data.game, - toUserId: data.trade.toUserId, + this.server.to(game.id).emit('updateGameData', { + fields, + game: updatedGame, + requestId: data.requestId, }); - this.server.to(data.game.id).emit('updateGameData', { game: updatedGame }); - this.server - .to(data.trade.toUserId) - .emit('tradeOffered', { trade: data.trade }); - this.gameService.setTimer( - updatedGame.id, - 10000, - updatedGame, - this.playerService.refuseFromTrade + } + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('sellBranch') + async onSellBranch( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { index: number; requestId: string } + ) { + const index = data.index; + const game = socket.game; + const userId = socket.data.jwtPayload.sub; + const { updatedGame, fields } = await this.gameService.sellBranch( + game, + index, + userId, + data.requestId ); + this.server.to(game.id).emit('updateGameData', { + fields, + game: updatedGame, + requestId: data.requestId, + }); } - @OnEvent('setRollDiceTimer') - async handleSetRollDiceTimer(game: Partial) { - this.gameService.setTimer(game.id, game.timeOfTurn, game, this.rollDice); + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('surrender') + async surrender( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { requestId: string } + ) { + const userId = socket.data.jwtPayload.sub; + const gameId = socket.game.id; + + const { updatedPlayer, updatedFields } = await this.gameService.loseGame( + userId, + gameId, + null, + data.requestId + ); + + this.server.to(gameId).emit('updateGameData', { + game: updatedPlayer.game, + fields: updatedFields, + requestId: data.requestId, + }); } - @OnEvent('setAfterRolledDiceTimer') - async handleSetAfterRolledDiceTimer(updatedGame: Partial) { - const currentPlayer = this.playerService.findPlayerWithTurn(updatedGame); - const fields = await this.gameService.getGameFields(updatedGame.id); - const playerNextField = this.gameService.findPlayerFieldByIndex( - fields, - currentPlayer.currentFieldIndex + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('mortgageField') + async onMortgageField( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { index: number; requestId: string } + ) { + const index = data.index; + const game = socket.game; + const { player, fields } = await this.gameService.mortgageField( + game, + index, + socket.data.jwtPayload.sub, + data.requestId ); - if ( - playerNextField.price && - playerNextField.ownedBy === updatedGame?.turnOfUserId - ) { - this.gameService.setTimer( - updatedGame.id, - 2500, - updatedGame, - this.passTurnToNext - ); - } - if ( - playerNextField.ownedBy && - playerNextField.ownedBy !== currentPlayer.userId - ) { - this.steppedOnPrivateField(currentPlayer, playerNextField, updatedGame); - return; - } - if (playerNextField.price && !playerNextField.ownedBy) { - this.gameService.setTimer( - updatedGame.id, - updatedGame.timeOfTurn, - updatedGame, - this.putUpForAuction - ); - } - if (!playerNextField.price) { - this.processSpecialField(updatedGame, playerNextField); - } - const currentField = - await this.gameService.findCurrentFieldWithUserId(updatedGame); - if ( - currentField?.specialField && - !currentField.secret && - !currentField.toPay - ) { - this.gameService.setTimer( - updatedGame.id, - 12000, - updatedGame, - this.passTurnToNext - ); - } + this.server.to(game.id).emit('updateGameData', { + fields, + game: player.game, + requestId: data.requestId, + }); } } diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 8b68399..29ecae6 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -1,38 +1,18 @@ -import { forwardRef, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { GameService } from './game.service'; import { GameGateway } from './game.gateway'; -import { GameRepository } from './game.repository'; -import { UserModule } from 'src/user/user.module'; -import { JwtModule } from '@nestjs/jwt'; -import { ConfigModule } from '@nestjs/config'; -import { PlayerModule } from 'src/player/player.module'; -import { ChatModule } from 'src/chat/chat.module'; -import { EventModule } from 'src/event/event.module'; -import { WebSocketServerModule } from 'src/webSocketServer/webSocketServer.module'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Field, FieldSchema } from 'src/schema/Field.schema'; import { GameController } from './game.controller'; -import { AuctionModule } from 'src/auction/auction.module'; +import { GameRepository } from './game.repository'; +import { GameRepositoryV2 } from './game.repository.v2'; @Module({ - controllers: [GameController], - imports: [ - UserModule, - JwtModule, - ConfigModule, - ChatModule, - forwardRef(() => PlayerModule), - EventModule, - WebSocketServerModule, - forwardRef(() => AuctionModule), - MongooseModule.forFeature([ - { - name: Field.name, - schema: FieldSchema, - }, - ]), + providers: [ + GameService, + GameGateway, + GameRepository, + GameRepositoryV2, ], - providers: [GameGateway, GameService, GameRepository], - exports: [GameService], + controllers: [GameController], + exports: [GameService, GameRepository, GameRepositoryV2], }) export class GameModule {} diff --git a/src/game/game.repository.v2.ts b/src/game/game.repository.v2.ts new file mode 100644 index 0000000..5638aa0 --- /dev/null +++ b/src/game/game.repository.v2.ts @@ -0,0 +1,209 @@ +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { + GameWithRelations, + GameWithPlayers, + CreateGameData, + StartGameData, + GameInclude, +} from './types/game.types'; +import { Game, Player, User, ChatType, Prisma } from '@prisma/client'; + +@Injectable() +export class GameRepositoryV2 { + private readonly logger = new Logger(GameRepositoryV2.name); + private readonly gameInclude: GameInclude = { + players: { + include: { + user: { + select: { + id: true, + nickname: true, + }, + }, + }, + orderBy: { + createdAt: 'asc', + }, + }, + properties: true, + currentPlayer: true, + winner: true, + chat: { + select: { + id: true, + }, + }, + }; + + constructor(private readonly prisma: PrismaService) {} + + async getVisibleGames(): Promise { + try { + return (await this.prisma.game.findMany({ + where: { status: 'LOBBY' }, + include: this.gameInclude, + })) as unknown as GameWithRelations[]; + } catch (error) { + this.logger.error('Failed to fetch visible games', error); + throw new Error('Failed to fetch visible games'); + } + } + + async createGameWithPlayer( + userId: string, + playersCapacity = 4 + ): Promise { + try { + const data: CreateGameData = { + playersCapacity, + players: { + create: { + userId, + color: '#000000', + }, + }, + turnEnds: '10000', + }; + + return (await this.prisma.game.create({ + data, + include: this.gameInclude, + })) as unknown as GameWithRelations; + } catch (error) { + this.logger.error('Failed to create game with player', error); + throw new Error('Failed to create game'); + } + } + + async getGameById(gameId: string): Promise { + try { + const game = (await this.prisma.game.findUnique({ + where: { id: gameId }, + include: this.gameInclude, + })) as unknown as GameWithRelations; + + if (!game) { + throw new NotFoundException(`Game with ID ${gameId} not found`); + } + + return game; + } catch (error) { + this.logger.error(`Failed to fetch game ${gameId}`, error); + throw error instanceof NotFoundException + ? error + : new Error('Failed to fetch game'); + } + } + + async startGame( + gameId: string, + turnEnds: string + ): Promise { + return this.prisma.$transaction(async (tx) => { + const gameWithPlayers = await tx.game.findUnique({ + where: { id: gameId }, + include: { + players: { + orderBy: { createdAt: 'asc' }, + }, + }, + }); + + if (!gameWithPlayers) { + throw new NotFoundException(`Game with ID ${gameId} not found`); + } + + if (gameWithPlayers.players.length < 2) { + throw new Error('At least 2 players are required to start the game'); + } + + const firstPlayer = + gameWithPlayers.players[gameWithPlayers.players.length - 1]; + + const updateData: StartGameData = { + status: 'ACTIVE', + turnOfUserId: firstPlayer.userId, + turnEnds, + chat: { + create: { + type: ChatType.GAME, + participants: { + createMany: { + data: gameWithPlayers.players.map((player) => ({ + userId: player.userId, + })), + }, + }, + }, + }, + }; + + return (await tx.game.update({ + where: { id: gameId }, + data: updateData, + include: this.gameInclude, + })) as unknown as GameWithRelations; + }); + } + + async updateGame( + gameId: string, + data: Prisma.GameUpdateInput + ): Promise { + try { + return (await this.prisma.game.update({ + where: { id: gameId }, + data, + include: this.gameInclude, + })) as unknown as GameWithRelations; + } catch (error) { + this.logger.error(`Failed to update game ${gameId}`, error); + throw new Error(`Failed to update game: ${error.message}`); + } + } + + async increaseHouses( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { housesQty: { increment: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } + + async decreaseHotels( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { hotelsQty: { decrement: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } + + async increaseHotels( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { hotelsQty: { increment: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } + + async decreaseHouses( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { housesQty: { decrement: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } +} diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 4f36765..a9652cb 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -1,58 +1,71 @@ -import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; -import { ChatType, Player, Prisma } from '@prisma/client'; -import { Model } from 'mongoose'; -import { EventService } from 'src/event/event.service'; +import { Prisma } from '@prisma/client'; +import { AuctionService } from 'src/auction/auction.service'; +import { HandlerChain } from 'src/common/handlerChain'; +import { FieldService } from 'src/field/field.service'; +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { PaymentService } from 'src/payment/payment.service'; import { PlayerService } from 'src/player/player.service'; -import { Field, FieldDocument } from 'src/schema/Field.schema'; +import { FieldDocument } from 'src/schema/Field.schema'; +import { SecretService } from 'src/secret/secret.service'; +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { TimerService } from 'src/timer/timers.service'; import { DEFAULT_FIELDS } from 'src/utils/fields'; -import secretFields, { SecretType } from 'src/utils/fields/secretFields'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; import { GamePayload, GameRepository } from './game.repository'; -import { SecretInfo } from './types/secretInfo.type'; -import { AuctionService } from 'src/auction/auction.service'; +import { GameRepositoryV2 } from 'src/game/game.repository.v2'; +import { PutUpForAuctionHandler } from 'src/game/handlers/putUpForAuction.handler'; +import { PassTurnHandler } from 'src/game/handlers/passTurn.handler'; +import { ProcessSpecialHandler } from 'src/game/handlers/processSpecial.handler'; +import { SteppedOnPrivateHandler } from 'src/game/handlers/steppedOnPrivate.handler'; + @Injectable() export class GameService { constructor( - private gameRepository: GameRepository, @Inject(forwardRef(() => PlayerService)) private playerService: PlayerService, - private eventService: EventService, + private webSocketProvider: WebSocketProvider, + private gameRepository: GameRepository, @Inject(forwardRef(() => AuctionService)) private auctionService: AuctionService, - @InjectModel(Field.name) - private fieldModel: Model + private timerService: TimerService, + private fieldService: FieldService, + @Inject(forwardRef(() => SecretService)) + private secretService: SecretService, + private paymentService: PaymentService, + private gameRepositoryV2: GameRepositoryV2 ) { this.passTurnToUser = this.passTurnToUser.bind(this); + this.rollDice = this.rollDice.bind(this); + this.putUpForAuction = this.putUpForAuction.bind(this); + this.passTurn = this.passTurn.bind(this); + this.processPayingForField = this.processPayingForField.bind(this); + this.transferWithBank = this.transferWithBank.bind(this); + this.payAll = this.payAll.bind(this); + this.payForPrivateField = this.payForPrivateField.bind(this); + this.resolveTwoUsers = this.resolveTwoUsers.bind(this); } readonly PLAYING_FIELDS_QUANTITY = 40; - readonly logger = new Logger(GameService.name); - timers: Map void }> = - new Map(); - readonly secrets: Map = new Map(); - async getVisibleGames() { - return this.gameRepository.findMany({ - where: { status: 'LOBBY' }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + return this.gameRepositoryV2.getVisibleGames(); + } + + async getAllGameData(gameId: string, requestId: string) { + try { + const game = await this.getGame(gameId); + const auction = this.auctionService.auctions.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); + const fields = await this.fieldService.getGameFields(game.id); + return { game, auction, secretInfo, fields }; + } catch (error) { + throw new WsException({ message: 'Coundlt get game data', requestId }); + } } - async createGame(userId: string) { + async createGame(userId: string, requestId?: string) { const activePlayer = await this.gameRepository.findFirst({ where: { OR: [{ status: 'LOBBY' }, { status: 'ACTIVE' }], @@ -64,71 +77,42 @@ export class GameService { }, }); - if (activePlayer) return null; - const newGame = await this.gameRepository.create({ - data: { - playersCapacity: 4, // TODO change players capacity to dynamic number - players: { - create: { - userId, - color: this.playerService.COLORS[0], - }, - }, - turnEnds: '10000', - }, - include: { - players: { - include: { - user: { - select: { - nickname: true, - }, - }, - }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + if (activePlayer) + throw new WsException({ + message: 'You already have active game', + requestId, + }); + const newGame = await this.gameRepositoryV2.createGameWithPlayer(userId); + const gameFields = DEFAULT_FIELDS.map((field) => ({ ...field, gameId: newGame.id, })); - await this.fieldModel.insertMany(gameFields); + await this.fieldService.createMany(gameFields); return newGame; } - async getGameFields(gameId: string) { - return await this.fieldModel.find({ gameId }); + async getGame(gameId: string) { + return this.gameRepositoryV2.getGameById(gameId); } - async getGame(gameId: string) { - return this.gameRepository.findUnique({ - where: { id: gameId }, - include: { - players: { - include: { user: { select: { nickname: true, id: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, + async joinGame(gameId: string, userId: string, requestId: string) { + const playerWithUserId = await this.playerService.findFirst({ + where: { + userId, + game: { + status: { + in: ['LOBBY', 'ACTIVE'], }, }, }, }); - } - - async onJoinGame(gameId: string, userId: string) { + if (playerWithUserId) + throw new WsException({ + message: 'You already in game, leave it first', + requestId, + }); const game = await this.gameRepository.findFirst({ where: { id: gameId }, include: { @@ -148,7 +132,10 @@ export class GameService { (player) => player.userId === userId ); if (alreadyJoined || game.status !== 'LOBBY') - return { game: null, shouldStart: false }; + throw new WsException({ + message: 'You cannot join this game', + requestId, + }); const color = this.playerService.COLORS[game.players.length]; const player = await this.playerService.create({ color, @@ -160,124 +147,276 @@ export class GameService { gameWithCreatedPlayer.playersCapacity === gameWithCreatedPlayer.players.length ) { - const randomPlayerIndex = Math.floor( - Math.random() * gameWithCreatedPlayer.players.length - ); - const turnEnds = this.calculateEndOfTurn(game.timeOfTurn); - const startedGame = await this.gameRepository.updateById(gameId, { - data: { - status: 'ACTIVE', - turnOfUserId: - gameWithCreatedPlayer.players[ - gameWithCreatedPlayer.players.length - 1 - ].userId, - turnEnds, - chat: { - create: { - type: ChatType.GAME, - participants: { - createMany: { - data: [ - ...gameWithCreatedPlayer.players.map((player) => ({ - userId: player.userId, - })), - ], - }, - }, - }, - }, - }, - include: { - players: { - include: { user: { select: { nickname: true, id: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); - return { game: startedGame, shouldStart: true }; + const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); + return this.gameRepositoryV2.startGame(gameId, turnEnds); } - return { game: gameWithCreatedPlayer, shouldStart: false }; + return gameWithCreatedPlayer; + } + + startGame(game: Partial) { + // We can setTimeout here for some countdown on frontend + this.webSocketProvider.server.to(game.id).emit('startGame', { + game, + chatId: game.chat.id, + }); + this.webSocketProvider.server.emit('clearStartedGame', { + gameId: game.id, + }); + this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } - async onLeaveGame(gameId: string, userId: string) { + async leaveGame(gameId: string, userId: string, requestId: string) { const player = await this.playerService.findFirst({ where: { userId, gameId }, }); - if (!player) return null; + if (!player) + throw new WsException({ message: 'You are not in this game', requestId }); await this.playerService.deleteById(player.id); const game = await this.getGame(gameId); + if (!game.players.length) { + await this.gameRepository.delete({ + where: { id: gameId }, + include: { players: true }, + }); + } return game; } - async findGameWithPlayers(gameId: string) { - const game = await this.gameRepository.findUnique({ - where: { id: gameId }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, + getRandomDicesString() { + const firstDice = Math.ceil(Math.random() * 6); + const secondDice = Math.ceil(Math.random() * 6); + return `${firstDice}:${secondDice}`; + } + + async rollDice(game: Partial, requestId?: string) { + if (game.dices) + throw new WsException({ + message: 'You have already rolled dices', + requestId, + }); + this.timerService.clear(game.id); + const { playerNextField, fields, updatedGame } = await this.makeTurn(game); + await this.processRolledDices( + updatedGame, + playerNextField, + fields, + requestId + ); + } + + private async handlePaymentField( + game: Partial, + field: FieldDocument + ) { + this.timerService.set( + game.id, + game.timeOfTurn, + { game, userId: game.turnOfUserId, amount: field.toPay }, + this.transferWithBank + ); + } + + private async handleSecretField(game: Partial) { + const { message, secretInfo } = + await this.secretService.handleSecretWithMessage(game); + + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.webSocketProvider.server.to(game.id).emit('secret', secretInfo); + + await this.processSecretByUserCount(game, secretInfo.users.length); + } + + async processSpecialField( + game: Partial, + playerNextField: FieldDocument + ) { + if (playerNextField.toPay) { + await this.handlePaymentField(game, playerNextField); + } + + if (playerNextField.secret) { + await this.handleSecretField(game); + } + } + + async processRolledDices( + game: Partial, + playerNextField?: FieldDocument, + fields?: FieldDocument[], + requestId?: string + ) { + if (!playerNextField) { + playerNextField = await this.findCurrentFieldFromGame(game); + } + const fieldAnalyzer = new FieldAnalyzer( + playerNextField, + game, + this.playerService + ); + this.webSocketProvider.server.to(game.id).emit('rolledDice', { + ...(fields !== undefined && { fields }), + requestId, + game, }); - return game; + const chain = new HandlerChain(); + chain.addHandlers( + new PassTurnHandler(fieldAnalyzer, () => { + this.timerService.set(game.id, 2500, game, this.passTurn); + }), + new ProcessSpecialHandler(fieldAnalyzer, () => { + this.processSpecialField(game, playerNextField); + }), + new SteppedOnPrivateHandler(fieldAnalyzer, () => { + this.steppedOnPrivateField(fieldAnalyzer); + }), + new PutUpForAuctionHandler(fieldAnalyzer, () => { + this.timerService.set( + game.id, + game.timeOfTurn, + game, + this.putUpForAuction + ); + }) + ); + chain.process(); } - findById(gameId: string) { - return this.gameRepository.findById(gameId); + private async processSecretByUserCount( + game: Partial, + userCount: number + ) { + if (userCount === 1) { + return this.oneUserTransfer(game); + } + + if (userCount === 2) { + return this.twoUsersTransfer(game); + } + + if (userCount > 2) { + return this.multipleUsersTransfer(game); + } } - onRollDice() { - const firstDice = Math.ceil(Math.random() * 6); - const secondDice = Math.ceil(Math.random() * 6); - return `${firstDice}:${secondDice}`; + async oneUserTransfer(game: Partial) { + const secretInfo = this.secretService.secrets.get(game.id); + const secretAnalyzer = new SecretAnalyzer(secretInfo, game.turnOfUserId); + if (secretAnalyzer.isOneUserHaveToPay()) { + this.timerService.set( + game.id, + game.timeOfTurn, + { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, + this.transferWithBank + ); + } else { + this.timerService.set( + game.id, + 2000, + { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, + this.transferWithBank + ); + } + } + + private async twoUsersTransfer(game: Partial) { + this.timerService.set(game.id, game.timeOfTurn, game, this.resolveTwoUsers); + } + + private async multipleUsersTransfer(game: Partial) { + this.timerService.set(game.id, game.timeOfTurn, game, this.payAll); + } + + async payAll(game: Partial) { + const updatedGame = await this.secretService.payAllforSecret(game); + this.webSocketProvider.server.to(game.id).emit('updatePlayers', { + game: updatedGame, + }); + this.passTurn(updatedGame); + } + + async resolveTwoUsers(game: Partial) { + const { userId, loseGame, fields, updatedGame } = + await this.secretService.resolveTwoUsers(game); + let gameAfterLoss = null; + if (loseGame) { + gameAfterLoss = await this.loseGame(userId, game.id, fields); + } + this.passTurn(gameAfterLoss || updatedGame); + } + + async transferWithBank(argsObj: { + game: Partial; + amount: number; + userId: string; + requestId?: string; + }) { + const { updatedGame } = await this.paymentService.transferWithBank( + argsObj.game, + argsObj.userId, + argsObj.amount + ); + const secretInfo = this.secretService.secrets.get(updatedGame.id); + if (!secretInfo) { + await this.passTurn(updatedGame, argsObj.requestId); + } else { + this.webSocketProvider.server.to(updatedGame.id).emit('updatePlayers', { + game: updatedGame, + secretInfo, + requestId: argsObj.requestId, + }); + } } - setTimer( - id: string, - time: number, - args: T, - callback: (args: T) => Promise | R - ): Promise | null { - this.clearTimer(id); - return new Promise((resolve, reject) => { - const timer = setTimeout(async () => { - try { - const res: R = await callback(args); - resolve(res); - } catch (err: unknown) { - const error = err instanceof Error ? err : new Error(String(err)); - console.log('In Timer'); - console.log(error.message); - reject(error); - } - }, time); - this.timers.set(id, { timer, reject }); - this.logger.log(`Timer with id:${id} was set for ${time / 1000} seconds`); - }).catch(() => { - return null; + async payToBankForSpecialField( + userId: string, + game: Partial, + requestId?: string + ) { + const currentField = await this.findCurrentFieldWithUserId(game); + if (!game.dices && !currentField.toPay) + throw new WsException({ + message: 'You cant pay for that field because its not special field', + requestId, + }); + return this.transferWithBank({ + game: game, + userId, + amount: currentField.toPay, + requestId, }); } - calculateEndOfTurn(timeOfTurn: number) { - let turnEnds = Date.now(); - turnEnds += timeOfTurn; - return turnEnds.toString(); + async steppedOnPrivateField({ currentPlayer, field, game }: FieldAnalyzer) { + const fields = await this.fieldService.getGameFields(game.id); + if ( + this.playerService.estimateAssets(currentPlayer, fields) >= + field.income[field.amountOfBranches] + ) { + this.timerService.set( + game.id, + game.timeOfTurn, + game, + this.payForPrivateField + ); + return; + } + const { updatedPlayer } = await this.loseGame( + currentPlayer.userId, + game.id, + fields + ); + await this.passTurn(updatedPlayer.game); + } + + async putUpForAuction(game: Partial, requestId?: string) { + const auction = await this.auctionService.putUpForAuction(game, requestId); + const updatedGame = await this.updateGameWithNewTurn(game, 15000); + this.webSocketProvider.server + .to(game.id) + .emit('hasPutUpForAuction', { game: updatedGame, auction, requestId }); + this.timerService.set(game.id, 15000, updatedGame, this.passTurn); } async findCurrentFieldFromGame(game: Partial) { @@ -285,15 +424,18 @@ export class GameService { game.turnOfUserId, game.id ); - const fields = await this.getGameFields(game.id); - return this.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const fields = await this.fieldService.getGameFields(game.id); + return this.fieldService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); } async updateGameWithNewTurn( game: Partial, timeOfTurn: number | null = null ) { - const turnEnds = this.calculateEndOfTurn( + const turnEnds = this.timerService.calculateFutureTime( timeOfTurn ? timeOfTurn : game.timeOfTurn ); return this.updateById(game.id, { @@ -313,36 +455,11 @@ export class GameService { return { turnOfNextUserId }; } - clearTimer(gameId: string) { - if (this.timers.has(gameId)) { - const timerObj = this.timers.get(gameId); - timerObj.reject('Timer cleared'); - clearTimeout(timerObj.timer); - this.timers.delete(gameId); - this.logger.log(`Cleared timer for game ${gameId}.`); - } - } - async updateById( gameId: string, fieldsToUpdate: Partial - ) { - return this.gameRepository.updateById(gameId, { - data: fieldsToUpdate, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + ): Promise { + return this.gameRepositoryV2.updateGame(gameId, fieldsToUpdate); } parseDicesToArr(dices: string) { @@ -366,10 +483,6 @@ export class GameService { return resObj; } - findPlayerFieldByIndex(fields: FieldDocument[], indexOfField: number) { - return fields.find((field) => field.index === indexOfField); - } - async decrementPledgedFields(fields: FieldDocument[]) { fields.forEach((field) => { if (field.isPledged && field.turnsToUnpledge === 0) { @@ -379,42 +492,19 @@ export class GameService { field.turnsToUnpledge--; } }); - await this.updateFields(fields, [ + await this.fieldService.updateFields(fields, [ 'isPledged', 'ownedBy', 'turnsToUnpledge', ]); } - async updateFields( - fields: T[], - propertiesToUpdate: string[] - ): Promise { - const updates = fields.map((field) => { - const updateFields: any = {}; - - for (const property of propertiesToUpdate) { - if (field[property] !== undefined) { - updateFields[property] = field[property]; - } - } - - return { - updateOne: { - filter: { _id: field._id }, - update: { $set: updateFields }, - }, - }; - }); - await this.fieldModel.bulkWrite(updates); - } - async makeTurn(game: Partial) { - const dices = this.onRollDice(); + const dices = this.getRandomDicesString(); const currentPlayer = this.playerService.findPlayerWithTurn(game); const dicesArr = this.parseDicesToArr(dices); - const fields = await this.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); const { nextIndex, shouldGetMoney } = this.calculateNextIndex( currentPlayer.currentFieldIndex, dicesArr, @@ -426,10 +516,13 @@ export class GameService { money: { increment: shouldGetMoney ? game.passStartBonus : 0 }, }); - const playerNextField = this.findPlayerFieldByIndex(fields, nextIndex); + const playerNextField = this.fieldService.findPlayerFieldByIndex( + fields, + nextIndex + ); let updatedGame: null | Partial = null; if (playerNextField.ownedBy !== currentPlayer.userId) { - const turnEnds = this.calculateEndOfTurn(game.timeOfTurn); + const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); updatedGame = await this.updateById(game.id, { dices, turnEnds, @@ -439,77 +532,40 @@ export class GameService { dices, }); } + return { updatedGame, fields, - nextIndex, playerNextField, - hasOwner: playerNextField?.ownedBy, - currentPlayer, }; } - getRandomSecret() { - const randomSecretIndex = Math.floor(Math.random() * secretFields.length); - return secretFields[randomSecretIndex]; - } - - async parseAndSaveSecret(secret: SecretType, game: Partial) { - if (secret.numOfPlayersInvolved === 'one') { - const secretInfo = { - amounts: secret.amounts, - users: [game.turnOfUserId], - }; - this.secrets.set(game.id, secretInfo); - return secretInfo; - } else if (secret.numOfPlayersInvolved === 'two') { - const playersWithoutActive = game.players.filter( - (player) => player.userId !== game.turnOfUserId && !player.lost - ); - const randomUserId = this.getRandomPlayersUserId(playersWithoutActive); - const secretInfo = { - amounts: secret.amounts, - users: [game.turnOfUserId, randomUserId], - }; - this.secrets.set(game.id, secretInfo); - return secretInfo; - } else if (secret.numOfPlayersInvolved === 'all') { - const secretInfo = { - amounts: secret.amounts, - users: [ - game.turnOfUserId, - ...game.players - .filter((player) => player.userId !== game.turnOfUserId) - .map((player) => { - if (!player.lost && player.userId !== game.turnOfUserId) { - return player.userId; - } - return ''; - }), - ], - }; - this.secrets.set(game.id, secretInfo); - return secretInfo; - } - } - - getRandomPlayersUserId(players: Partial) { - const randomIndex = Math.floor(Math.random() * players.length); - return players[randomIndex].userId; - } - async findCurrentFieldWithUserId(game: Partial) { const player = this.playerService.findPlayerWithTurn(game); - const fields = await this.getGameFields(game.id); - return this.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const fields = await this.fieldService.getGameFields(game.id); + return this.fieldService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); } - async buyField(game: Partial) { + async buyField(game: Partial, requestId: string) { + await this.processBuyField(game, requestId); + this.passTurn(game, requestId); + } + + async processBuyField(game: Partial, requestId: string) { const player = this.playerService.findPlayerWithTurn(game); - const fields = await this.getGameFields(game.id); - const field = this.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const fields = await this.fieldService.getGameFields(game.id); + const field = this.fieldService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); if (field.ownedBy) { - throw new WsException('Field is already owned'); + throw new WsException({ + message: 'This field is already owned', + requestId, + }); } if ( !field.price || @@ -517,11 +573,11 @@ export class GameService { field.ownedBy === player.userId || this.auctionService.getAuction(game.id) ) { - throw new WsException('You cant buy this field'); + throw new WsException({ message: 'You cant buy this field', requestId }); } - this.clearTimer(game.id); + this.timerService.clear(game.id); field.ownedBy = game.turnOfUserId; - await this.updateFields(fields, ['ownedBy']); + await this.fieldService.updateFields(fields, ['ownedBy']); const updatedPlayer = await this.playerService.decrementMoneyWithUserAndGameId( game.turnOfUserId, @@ -535,13 +591,18 @@ export class GameService { return game.players.filter((player) => !player.lost); } - async passTurnToNext(game: Partial) { + async passTurn(game: Partial, requestId?: string) { + const fields = await this.fieldService.getGameFields(game.id); + if (!game.dices) { - throw new WsException('You have to roll dices first'); + throw new WsException({ + message: 'You have to roll dices first', + requestId, + }); } const dices = ''; this.auctionService.setAuction(game.id, null); - this.clearTimer(game.id); + this.timerService.clear(game.id); let { turnOfNextUserId } = this.findNextTurnUser(game); game.turnOfUserId = turnOfNextUserId; let playersNotLost = game.players.length; @@ -554,16 +615,37 @@ export class GameService { } --playersNotLost; } - const turnEnds = this.calculateEndOfTurn(game.timeOfTurn); + const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); const updatedGame = await this.updateById(game.id, { turnOfUserId: turnOfNextUserId, dices, turnEnds, }); - return { updatedGame, turnEnds, turnOfNextUserId, dices }; + this.webSocketProvider.server + .to(game.id) + .emit('passTurnToNext', { game: updatedGame, fields, requestId }); + this.timerService.set(game.id, game.timeOfTurn, updatedGame, this.rollDice); + } + + async payForPrivateField(game: Partial, requestId?: string) { + const currentField = await this.findCurrentFieldWithUserId(game); + if (!game.dices || !currentField.ownedBy) + throw new WsException({ + message: 'You cant pay for that field', + requestId, + }); + this.timerService.clear(game.id); + const { updatedGame, fields: updatedFields } = + await this.processPayingForField(game, currentField); + this.webSocketProvider.server.to(game.id).emit('payedForField', { + requestId, + game: updatedGame, + fields: updatedFields, + }); + this.passTurn(updatedGame); } - async payForField( + async processPayingForField( game: Partial, playerNextField: FieldDocument ) { @@ -573,9 +655,9 @@ export class GameService { currentPlayer.money < playerNextField.income[playerNextField.amountOfBranches] ) { - const fields = await this.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); // We can add pledging of last owned field or smt to not make player lose immidiately - const { updatedPlayer } = await this.playerService.loseGame( + const { updatedPlayer } = await this.loseGame( currentPlayer.userId, game.id, fields @@ -597,135 +679,20 @@ export class GameService { return { updatedGame: received.game }; } - async payToBank(game: Partial, userId: string, amount: number) { - const secretInfo = this.secrets.get(game.id); - if (!secretInfo) this.clearTimer(game.id); - const currentPlayer = game.players.find( - (player) => player.userId === userId - ); - const fields = await this.getGameFields(game.id); - if (currentPlayer.money < amount) { - // We can add pledging of last owned field or smt to not make player lose immidiately - const { updatedPlayer, updatedFields } = - await this.playerService.loseGame( - currentPlayer.userId, - game.id, - fields - ); - return { updatedGame: updatedPlayer.game, fields: updatedFields }; - } - const playerWhoPayed = - await this.playerService.incrementMoneyWithUserAndGameId( - currentPlayer.userId || game.turnOfUserId, - game.id, - amount - ); - if (secretInfo && secretInfo.users.includes(userId)) { - const userIndex = secretInfo.users.findIndex( - (userId) => userId === playerWhoPayed.userId - ); - secretInfo.users[userIndex] = ''; - } - if ( - secretInfo && - secretInfo.users.every((userId, index) => { - if (secretInfo.users.length === 2 && userId !== '') { - return secretInfo.amounts[index] > 0; - } - if (secretInfo.users.length > 2 && index === 0) { - return true; - } - return userId === ''; - }) - ) { - this.secrets.delete(game.id); - return { - updatedGame: playerWhoPayed.game, - fields, - playerWhoPayed, - }; - } - return { - updatedGame: playerWhoPayed.game, - fields, - playerWhoPayed, - }; + async decreaseHouses(gameId: string, quantity: number) { + return this.gameRepositoryV2.decreaseHouses(gameId, quantity); } - decreaseHouses(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { housesQty: { decrement: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async increaseHouses(gameId: string, quantity: number) { + return this.gameRepositoryV2.increaseHouses(gameId, quantity); } - increaseHouses(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { housesQty: { increment: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async decreaseHotels(gameId: string, quantity: number) { + return this.gameRepositoryV2.decreaseHotels(gameId, quantity); } - decreaseHotels(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { hotelsQty: { decrement: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); - } - - increaseHotels(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { hotelsQty: { increment: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async increaseHotels(gameId: string, quantity: number) { + return this.gameRepositoryV2.increaseHotels(gameId, quantity); } hasWinner(game: Partial) { @@ -738,18 +705,195 @@ export class GameService { toUserId: string; turnTime?: number; }) { - this.clearTimer(data.game.id); - const turnEnds = this.calculateEndOfTurn(data.turnTime || 10000); + this.timerService.clear(data.game.id); + const turnEnds = this.timerService.calculateFutureTime( + data.turnTime || 10000 + ); const updatedGame = await this.updateById(data.game.id, { turnOfUserId: data.toUserId, turnEnds, }); - if (!data.game.dices) { - this.eventService.emitGameEvent('setRollDiceTimer', updatedGame); + if (!updatedGame.dices) { + this.timerService.set( + updatedGame.id, + updatedGame.timeOfTurn, + updatedGame, + this.rollDice + ); } else { - this.eventService.emitGameEvent('setAfterRolledDiceTimer', updatedGame); + this.processRolledDices(updatedGame); } - return { updatedGame }; } + + async buyBranch( + game: Partial, + index: number, + userId: string, + requestId: string + ) { + const fieldToBuyBranch = + await this.playerService.checkWhetherPlayerHasAllGroup( + game, + index, + userId, + true, + requestId + ); + this.playerService.checkFieldHasMaxBranches(fieldToBuyBranch, requestId); + const playerToPay = game.players.find((player) => player.userId === userId); + if (playerToPay.money < fieldToBuyBranch.branchPrice) { + throw new WsException({ + message: 'You dont have enough money to buy branch', + requestId, + }); + } + const player = await this.playerService.decrementMoneyWithUserAndGameId( + userId, + game.id, + fieldToBuyBranch.branchPrice + ); + fieldToBuyBranch.amountOfBranches++; + let updatedGame = null; + if (fieldToBuyBranch.amountOfBranches === 5) { + await this.decreaseHotels(player.game.id, 1); + updatedGame = await this.increaseHouses(player.game.id, 4); + } else { + updatedGame = await this.decreaseHouses(player.game.id, 1); + } + await this.fieldService.updateById(fieldToBuyBranch._id, { + amountOfBranches: fieldToBuyBranch.amountOfBranches, + }); + const fields = await this.fieldService.getGameFields(game.id); + return { updatedGame, fields }; + } + + async sellBranch( + game: Partial, + index: number, + userId: string, + requestId: string + ) { + const fieldToSellBranch = + await this.playerService.checkWhetherPlayerHasAllGroup( + game, + index, + userId, + false, + requestId + ); + this.playerService.checkFieldHasBranches(fieldToSellBranch, requestId); + const player = await this.playerService.incrementMoneyWithUserAndGameId( + userId, + game.id, + fieldToSellBranch.sellBranchPrice + ); + fieldToSellBranch.amountOfBranches--; + let updatedGame = null; + if (fieldToSellBranch.amountOfBranches === 4) { + await this.increaseHotels(player.game.id, 1); + updatedGame = await this.decreaseHouses(player.game.id, 4); + } else { + updatedGame = await this.increaseHouses(player.game.id, 1); + } + await this.fieldService.updateById(fieldToSellBranch._id, { + amountOfBranches: fieldToSellBranch.amountOfBranches, + }); + const fields = await this.fieldService.getGameFields(game.id); + return { updatedGame, fields }; + } + + async loseGame( + userId: string, + gameId: string, + fields?: FieldDocument[], + requestId?: string + ) { + if (!fields) { + fields = await this.fieldService.getGameFields(gameId); + } + this.timerService.clear(gameId); + const updatedPlayer = await this.playerService.updateLostGame( + userId, + gameId + ); + const updatedGame = await this.updateById(gameId, { + dices: 'playerLost', + }); + fields.forEach((field) => { + if (field.ownedBy === updatedPlayer.userId) { + field.ownedBy = null; + field.amountOfBranches = 0; + field.isPledged = false; + field.turnsToUnpledge = null; + } + }); + await this.fieldService.updateFields(fields, [ + 'ownedBy', + 'amountOfBranches', + 'isPledged', + 'turnsToUnpledge', + ]); + if (this.hasWinner(updatedPlayer.game)) { + this.timerService.clear(updatedPlayer.game.id); + const game = await this.updateById(updatedPlayer.game.id, { + status: 'FINISHED', + }); + this.webSocketProvider.server.to(game.id).emit('playerWon', { game }); + return { updatedPlayer, fields }; + } + this.passTurn(updatedGame, requestId); + return { updatedPlayer, updatedFields: fields }; + } + + mortgageField( + game: Partial, + index: number, + userId: string, + requestId?: string + ) { + const auction = this.auctionService.auctions.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); + if (auction || secretInfo) + throw new WsException({ + message: 'You cannot pledge field right now', + requestId, + }); + return this.playerService.pledgeField(game, index, userId, requestId); + } + + async payToUserForSecret( + game: Partial, + userId: string, + requestId?: string + ) { + const { + game: updatedGame, + secretInfo, + loseGame, + } = await this.secretService.payToUserForSecret(game, userId, requestId); + let gameAfterLoss = null; + if (loseGame) { + gameAfterLoss = await this.loseGame(userId, game.id); + } + return { game: loseGame ? gameAfterLoss : updatedGame, secretInfo }; + } + + async payToBankForSecret( + game: Partial, + userId: string, + requestId?: string + ) { + const amountToPay = await this.secretService.payToBankForSecret( + game, + userId, + requestId + ); + await this.transferWithBank({ + game: game, + userId, + amount: amountToPay, + requestId, + }); + } } diff --git a/src/game/handlers/AllPlayersInvolved.handler.ts b/src/game/handlers/AllPlayersInvolved.handler.ts new file mode 100644 index 0000000..cecba3c --- /dev/null +++ b/src/game/handlers/AllPlayersInvolved.handler.ts @@ -0,0 +1,14 @@ +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +import { WsException } from '@nestjs/websockets'; +export class AllPlayersInvolvedHandler extends BaseHandler { + throw() { + throw new WsException({ + message: 'You get money from all u dont have to pay', + requestId: this.analyzer.requestId, + }); + } + canHandle() { + return this.analyzer.isAllPlayersInvolved() || this.throw(); + } +} diff --git a/src/game/handlers/onePlayerInvolved.handler.ts b/src/game/handlers/onePlayerInvolved.handler.ts new file mode 100644 index 0000000..580e2f9 --- /dev/null +++ b/src/game/handlers/onePlayerInvolved.handler.ts @@ -0,0 +1,18 @@ +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +import { WsException } from '@nestjs/websockets'; +export class OnePlayerInvolvedHandler extends BaseHandler { + canHandle() { + return this.analyzer.inOnePlayerInvolved(); + } + + handle() { + if (this.analyzer.secretInfo.amounts[0] > 0) { + throw new WsException({ + message: + 'You cant pay to bank because one user and he doesnt have to pay', + requestId: this.analyzer.requestId, + }); + } + } +} diff --git a/src/game/handlers/passTurn.handler.ts b/src/game/handlers/passTurn.handler.ts new file mode 100644 index 0000000..9949e7a --- /dev/null +++ b/src/game/handlers/passTurn.handler.ts @@ -0,0 +1,11 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class PassTurnHandler extends BaseHandler { + canHandle() { + return ( + this.analyzer.isOwnedByCurrentUser() || + (this.analyzer.isNotOwned() && !this.analyzer.isAffordableForSomeone()) || + this.analyzer.isSkipable() + ); + } +} diff --git a/src/game/handlers/processSpecial.handler.ts b/src/game/handlers/processSpecial.handler.ts new file mode 100644 index 0000000..8fd218e --- /dev/null +++ b/src/game/handlers/processSpecial.handler.ts @@ -0,0 +1,7 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class ProcessSpecialHandler extends BaseHandler { + canHandle() { + return this.analyzer.isSpecialField(); + } +} diff --git a/src/game/handlers/putUpForAuction.handler.ts b/src/game/handlers/putUpForAuction.handler.ts new file mode 100644 index 0000000..48dd086 --- /dev/null +++ b/src/game/handlers/putUpForAuction.handler.ts @@ -0,0 +1,7 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class PutUpForAuctionHandler extends BaseHandler { + canHandle() { + return this.analyzer.isNotOwned() && this.analyzer.isAffordableForSomeone(); + } +} diff --git a/src/game/handlers/steppedOnPrivate.handler.ts b/src/game/handlers/steppedOnPrivate.handler.ts new file mode 100644 index 0000000..cf48e15 --- /dev/null +++ b/src/game/handlers/steppedOnPrivate.handler.ts @@ -0,0 +1,7 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class SteppedOnPrivateHandler extends BaseHandler { + canHandle() { + return this.analyzer.isOwnedByOtherAndNotPledged(); + } +} diff --git a/src/game/handlers/twoPlayersInvolved.handler.ts b/src/game/handlers/twoPlayersInvolved.handler.ts new file mode 100644 index 0000000..c3439e9 --- /dev/null +++ b/src/game/handlers/twoPlayersInvolved.handler.ts @@ -0,0 +1,29 @@ +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +import { WsException } from '@nestjs/websockets'; +import { SecretService } from 'src/secret/secret.service'; +export class TwoPlayersInvolvedHandler extends BaseHandler { + constructor( + analyzer: SecretAnalyzer, + private secretService: SecretService + ) { + super(analyzer); + } + canHandle() { + return this.analyzer.isTwoPlayersInvolved(); + } + + handle(): void { + const index = this.secretService.findIndexOfUserIdInSecretInfo( + this.analyzer.secretInfo, + this.analyzer.userId + ); + if (this.analyzer.secretInfo.amounts[index] > 0) { + throw new WsException({ + message: + 'You cant pay to bank two users and the one wants to pay dont have to', + requestId: this.analyzer.requestId, + }); + } + } +} diff --git a/src/game/test/game.service.ispec.ts b/src/game/test/game.service.ispec.ts index 74df0ab..a2e11a7 100644 --- a/src/game/test/game.service.ispec.ts +++ b/src/game/test/game.service.ispec.ts @@ -1,24 +1,23 @@ -import {GameRepository} from "../game.repository"; -import {GameService} from "../game.service"; -import {PrismaService} from "../../prisma/prisma.service"; -import {UserModule} from "../../user/user.module"; -import {PlayerModule} from "../../player/player.module"; -import {Test} from "@nestjs/testing"; -import {JwtModule} from "@nestjs/jwt"; -import {ConfigModule} from "@nestjs/config"; -import {GameGateway} from "../game.gateway"; -import {WsException} from "@nestjs/websockets"; -import {fields} from "../../utils/fields"; - +import { GameRepository } from '../game.repository'; +import { GameService } from '../game.service'; +import { PrismaService } from '../../prisma/prisma.service'; +import { UserModule } from '../../user/user.module'; +import { PlayerModule } from '../../player/player.module'; +import { Test } from '@nestjs/testing'; +import { JwtModule } from '@nestjs/jwt'; +import { ConfigModule } from '@nestjs/config'; +import { GameGateway } from '../game.gateway'; +import { WsException } from '@nestjs/websockets'; +import { fields } from '../../utils/fields'; describe('GameService', () => { let gameService: GameService; let prismaService: PrismaService; beforeAll(async () => { - const module = await Test.createTestingModule({ + const module = await Test.createTestingModule({ imports: [UserModule, JwtModule, ConfigModule, PlayerModule], - providers: [GameGateway, GameService, GameRepository] + providers: [GameGateway, GameService, GameRepository], }).compile(); gameService = module.get(GameService); @@ -31,14 +30,25 @@ describe('GameService', () => { playersCapacity: 2, }, }); - }) + }); it('should allow a player to join a game and start the game if capacity is reached', async () => { - const game = await prismaService.game.findFirst({}); - const user1 = await prismaService.user.create({ data: { nickname: 'User1', email: 'user1@example.com', hash: 'hashedPassword1' } }); - const user2 = await prismaService.user.create({ data: { nickname: 'User2', email: 'user2@example.com', hash: 'hashedPassword2' } }); + const user1 = await prismaService.user.create({ + data: { + nickname: 'User1', + email: 'user1@example.com', + hash: 'hashedPassword1', + }, + }); + const user2 = await prismaService.user.create({ + data: { + nickname: 'User2', + email: 'user2@example.com', + hash: 'hashedPassword2', + }, + }); await gameService.onJoinGame(game.id, user1.id); const result = await gameService.onJoinGame(game.id, user2.id); @@ -51,8 +61,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow( @@ -64,8 +74,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow( @@ -77,8 +87,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -88,8 +98,8 @@ describe('GameService', () => { data: { currentFieldIndex: 2, money: 1000, - } - }) + }, + }); await gameService.putUpForAuction(game); @@ -105,8 +115,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -115,8 +125,8 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); await prismaService.player.update({ where: { @@ -127,21 +137,23 @@ describe('GameService', () => { }, data: { money: 0, - } - }) + }, + }); await gameService.putUpForAuction(game); const auction = gameService.getAuction(game.id); expect(auction).toBeDefined(); - await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow(new WsException('Not enough money')); + await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow( + new WsException('Not enough money') + ); }); it('should throw an error if the raise amount is less than 100', async () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -150,8 +162,8 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); await gameService.putUpForAuction(game); @@ -164,8 +176,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -174,8 +186,8 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); await prismaService.player.update({ where: { @@ -186,7 +198,7 @@ describe('GameService', () => { }, data: { money: 50, - } + }, }); await gameService.putUpForAuction(game); @@ -204,17 +216,17 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); const user1 = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); const user2 = await prismaService.user.findFirst({ where: { - email: 'user2@example.com' - } + email: 'user2@example.com', + }, }); await prismaService.player.update({ @@ -226,7 +238,7 @@ describe('GameService', () => { }, data: { money: 5000, - } + }, }); await prismaService.player.update({ @@ -238,7 +250,7 @@ describe('GameService', () => { }, data: { money: 5000, - } + }, }); await gameService.putUpForAuction(game); @@ -253,13 +265,12 @@ describe('GameService', () => { expect(raisedAuction.auctionUpdated.userId).toEqual(user2.id); }); - it('should reset the auction timer after a price raise', async () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.update({ @@ -271,7 +282,7 @@ describe('GameService', () => { }, data: { money: 1000, - } + }, }); await gameService.putUpForAuction(game); @@ -286,7 +297,6 @@ describe('GameService', () => { }); describe('buyField tests', () => { - it('should allow a player to buy a field if they have enough money', async () => { const game = await prismaService.game.findFirst({ include: { players: true }, @@ -329,7 +339,10 @@ describe('GameService', () => { data: { money: 5000, currentFieldIndex: 2 }, }); - const field = gameService.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const field = gameService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); field.price = null; await expect(gameService.buyField(game as any)).rejects.toThrow( @@ -361,7 +374,6 @@ describe('GameService', () => { }); describe('makeTurn tests', () => { - it('should successfully make a turn and update the game state', async () => { const game = await prismaService.game.findFirst({ include: { players: true }, @@ -379,7 +391,7 @@ describe('GameService', () => { expect(result.updatedGame.turnEnds).toBeDefined(); }); - it('should update the player\'s field index based on the dice roll', async () => { + it("should update the player's field index based on the dice roll", async () => { const game = await prismaService.game.findFirst({ include: { players: true }, }); @@ -416,7 +428,9 @@ describe('GameService', () => { }); const result = await gameService.makeTurn(game as any); - expect(result.nextIndex).toBeLessThan(gameService.PLAYING_FIELDS_QUANTITY); + expect(result.nextIndex).toBeLessThan( + gameService.PLAYING_FIELDS_QUANTITY + ); }); it('should update the game state correctly after a turn', async () => { @@ -441,11 +455,23 @@ describe('GameService', () => { }); describe('GameService - passTurnToNext', () => { - it('should pass the turn to the next player and reset the auction state', async () => { - - const user1 = await prismaService.user.create({ data: { id: 'user3-id', nickname: 'User3', email: 'user3@example.com', hash: 'hashedPassword1' } }); - const user2 = await prismaService.user.create({ data: { id: 'user4-id', nickname: 'User4', email: 'user4@example.com', hash: 'hashedPassword2' } }); + const user1 = await prismaService.user.create({ + data: { + id: 'user3-id', + nickname: 'User3', + email: 'user3@example.com', + hash: 'hashedPassword1', + }, + }); + const user2 = await prismaService.user.create({ + data: { + id: 'user4-id', + nickname: 'User4', + email: 'user4@example.com', + hash: 'hashedPassword2', + }, + }); const game = await prismaService.game.create({ data: { @@ -493,12 +519,20 @@ describe('GameService', () => { data: { money: 5000, currentFieldIndex: 2 }, }); - const field = { index: 2, ownedBy: player2.userId, incomeWithoutBranches: 200 }; + const field = { + index: 2, + ownedBy: player2.userId, + incomeWithoutBranches: 200, + }; const result = await gameService.payForField(game as any, field as any); - const updatedPlayer1 = await prismaService.player.findUnique({ where: { id: player1.id } }); - const updatedPlayer2 = await prismaService.player.findUnique({ where: { id: player2.id } }); + const updatedPlayer1 = await prismaService.player.findUnique({ + where: { id: player1.id }, + }); + const updatedPlayer2 = await prismaService.player.findUnique({ + where: { id: player2.id }, + }); expect(updatedPlayer1.money).toBe(4800); // 5000 - 200 expect(updatedPlayer2.money).toBe(5200); // 5000 + 200 @@ -515,8 +549,8 @@ describe('GameService', () => { await prismaService.game.update({ where: { id: game.id }, data: { turnOfUserId: player1.userId }, - }) - + }); + await prismaService.player.update({ where: { id: player1.id }, data: { money: 100, currentFieldIndex: 2 }, @@ -525,13 +559,17 @@ describe('GameService', () => { const updatedGame = await prismaService.game.findFirst({ where: { id: game.id }, include: { players: true }, - }) - + }); - const field = { index: 2, ownedBy: player2.userId, incomeWithoutBranches: 200 }; + const field = { + index: 2, + ownedBy: player2.userId, + incomeWithoutBranches: 200, + }; - await expect(gameService.payForField(updatedGame as any, field as any)).rejects.toThrow(WsException); + await expect( + gameService.payForField(updatedGame as any, field as any) + ).rejects.toThrow(WsException); }); - }); -}); \ No newline at end of file +}); diff --git a/src/game/types/auction.type.ts b/src/game/types/auction.type.ts index 1f20be3..955a303 100644 --- a/src/game/types/auction.type.ts +++ b/src/game/types/auction.type.ts @@ -5,6 +5,7 @@ export interface Bidder { } export interface Auction { + bidTimeSec: number; fieldIndex: number; bidders: Bidder[]; turnEnds: string; diff --git a/src/game/types/game.types.ts b/src/game/types/game.types.ts new file mode 100644 index 0000000..f3c646c --- /dev/null +++ b/src/game/types/game.types.ts @@ -0,0 +1,68 @@ +import { Game, Player, User, GameStatus, ChatType } from '@prisma/client'; + +export type GameWithRelations = Game & { + players: (Player & { + user: Pick; + })[]; + currentPlayer: Player | null; + winner: User | null; + chat?: { + id: string; + }; +}; + +export type GameWithPlayers = Game & { + players: Player[]; + currentPlayer: Player | null; +}; + +export type CreateGameData = { + playersCapacity: number; + players: { + create: { + userId: string; + color: string; + }; + }; + turnEnds: string; +}; + +export type StartGameData = { + status: GameStatus; + turnOfUserId: string; + turnEnds: string; + chat: { + create: { + type: ChatType; + participants: { + createMany: { + data: Array<{ userId: string }>; + }; + }; + }; + }; +}; + +export type GameInclude = { + players: { + include: { + user: { + select: { + id: boolean; + nickname: boolean; + }; + }; + }; + orderBy: { + createdAt: 'asc'; + }; + }; + properties?: boolean; + currentPlayer?: boolean; + winner?: boolean; + chat?: { + select: { + id: boolean; + }; + }; +}; diff --git a/src/game/types/secretInfo.type.ts b/src/game/types/secretInfo.type.ts index 857c478..bd0180e 100644 --- a/src/game/types/secretInfo.type.ts +++ b/src/game/types/secretInfo.type.ts @@ -1,6 +1,8 @@ export interface SecretInfo { amounts: number[]; users: string[]; + text: string; + numOfPlayersInvolved: 'one' | 'two' | 'all'; } export type SecretPayments = SecretInfo[]; diff --git a/src/mail/mail.service.ts b/src/mail/mail.service.ts index c674b70..daddeec 100644 --- a/src/mail/mail.service.ts +++ b/src/mail/mail.service.ts @@ -11,9 +11,9 @@ export class MailService { async sendVerificationEmail(email: string, token: string) { const confirmationUrl = `${ - process.env.NODE_ENV === 'development' - ? process.env.FRONTEND_URL_DEV - : process.env.FRONTEND_URL_PROD + this.configService.get('NODE_ENV') === 'development' + ? this.configService.get('FRONTEND_URL_DEV') + : this.configService.get('FRONTEND_URL_PROD') }/auth/confirm-email/${token}`; await this.mailerService.sendMail({ to: email, @@ -25,9 +25,9 @@ export class MailService { async sendForgotPasswordEmail(email: string, token: string) { const forgotPasswordUrl = `${ - process.env.NODE_ENV === 'development' - ? process.env.FRONTEND_URL_DEV - : process.env.FRONTEND_URL_PROD + this.configService.get('NODE_ENV') === 'development' + ? this.configService.get('FRONTEND_URL_DEV') + : this.configService.get('FRONTEND_URL_PROD') }/auth/reset-password/${token}`; await this.mailerService.sendMail({ to: email, diff --git a/src/payment/payment.module.ts b/src/payment/payment.module.ts new file mode 100644 index 0000000..891db7e --- /dev/null +++ b/src/payment/payment.module.ts @@ -0,0 +1,20 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { PaymentService } from './payment.service'; +import { SecretModule } from 'src/secret/secret.module'; +import { FieldModule } from 'src/field/field.module'; +import { PlayerModule } from 'src/player/player.module'; +import { TimerModule } from 'src/timer/timers.module'; +import { GameModule } from 'src/game/game.module'; + +@Module({ + imports: [ + forwardRef(() => SecretModule), + FieldModule, + PlayerModule, + TimerModule, + forwardRef(() => GameModule), + ], + providers: [PaymentService], + exports: [PaymentService], +}) +export class PaymentModule {} diff --git a/src/payment/payment.service.ts b/src/payment/payment.service.ts new file mode 100644 index 0000000..e4caede --- /dev/null +++ b/src/payment/payment.service.ts @@ -0,0 +1,78 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { FieldService } from 'src/field/field.service'; +import { GamePayload } from 'src/game/game.repository'; +import { GameService } from 'src/game/game.service'; +import { PlayerService } from 'src/player/player.service'; +import { SecretService } from 'src/secret/secret.service'; +import { TimerService } from 'src/timer/timers.service'; + +@Injectable() +export class PaymentService { + constructor( + @Inject(forwardRef(() => SecretService)) + private secretService: SecretService, + private playerService: PlayerService, + private timerService: TimerService, + private fieldService: FieldService, + @Inject(forwardRef(() => GameService)) + private gameService: GameService + ) {} + + async transferWithBank( + game: Partial, + userId: string, + amount: number + ) { + const secretInfo = this.secretService.secrets.get(game.id); + if (!secretInfo) this.timerService.clear(game.id); + const currentPlayer = game.players.find( + (player) => player.userId === userId + ); + const fields = await this.fieldService.getGameFields(game.id); + if (currentPlayer.money < amount) { + // We can add pledging of last owned field or smt to not make player lose immidiately + const { updatedPlayer, updatedFields } = await this.gameService.loseGame( + currentPlayer.userId, + game.id, + fields + ); + return { updatedGame: updatedPlayer.game, fields: updatedFields }; + } + const playerWhoPayed = + await this.playerService.incrementMoneyWithUserAndGameId( + currentPlayer.userId || game.turnOfUserId, + game.id, + amount + ); + if (secretInfo && secretInfo.users.includes(userId)) { + const userIndex = secretInfo.users.findIndex( + (userId) => userId === playerWhoPayed.userId + ); + secretInfo.users[userIndex] = ''; + } + if ( + secretInfo && + secretInfo.users.every((userId, index) => { + if (secretInfo.users.length === 2 && userId !== '') { + return secretInfo.amounts[index] > 0; + } + if (secretInfo.users.length > 2 && index === 0) { + return true; + } + return userId === ''; + }) + ) { + this.secretService.secrets.delete(game.id); + return { + updatedGame: playerWhoPayed.game, + fields, + playerWhoPayed, + }; + } + return { + updatedGame: playerWhoPayed.game, + fields, + playerWhoPayed, + }; + } +} diff --git a/src/player/player.gateway.ts b/src/player/player.gateway.ts index 7d1ee69..3ef7a88 100644 --- a/src/player/player.gateway.ts +++ b/src/player/player.gateway.ts @@ -1,35 +1,18 @@ -import { - forwardRef, - Inject, - UseFilters, - UseGuards, - UsePipes, -} from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { ConnectedSocket, MessageBody, - OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketServer, - WsException, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; -import { HasLostGuard, TurnGuard } from 'src/auth/guard'; -import { ActiveGameGuard } from 'src/auth/guard/activeGame.guard'; -import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; -import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; -import { ChatService } from 'src/chat/chat.service'; -import { EventService } from 'src/event/event.service'; +import { TurnGuard, ValidPlayerGuard } from 'src/auth/guard'; import { GamePayload } from 'src/game/game.repository'; -import { GameService } from 'src/game/game.service'; -import { Trade } from 'src/game/types/trade.type'; import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; -import { WebSocketServerService } from 'src/webSocketServer/webSocketServer.service'; -import { OfferTradeDto } from './dto/offer-trade.dto'; import { PlayerService } from './player.service'; -import { AuctionService } from 'src/auction/auction.service'; +import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; @WebSocketGateway({ cors: { @@ -43,202 +26,31 @@ import { AuctionService } from 'src/auction/auction.service'; }) @UseFilters(WebsocketExceptionsFilter) @UsePipes(new WsValidationPipe()) -@UseGuards(WsGuard, ActiveGameGuard, HasLostGuard) -export class PlayerGateway implements OnGatewayInit { - constructor( - private readonly playerService: PlayerService, - private eventService: EventService, - private webSocketServerService: WebSocketServerService, - @Inject(forwardRef(() => GameService)) - private readonly gameService: GameService, - private chatService: ChatService, - @Inject(forwardRef(() => AuctionService)) - private auctionService: AuctionService - ) {} +export class PlayerGateway { + constructor(private readonly playerService: PlayerService) {} @WebSocketServer() private server: Server; - afterInit(server: Server) { - this.webSocketServerService.setServer(server); - } - @SubscribeMessage('buyBranch') - async buyBranch( + @UseGuards(ValidPlayerGuard, TurnGuard) + @SubscribeMessage('unmortgageField') + async onUnmortgageField( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody('index') index: number - ) { - const game = socket.game; - const userId = socket.jwtPayload.sub; - const fieldToBuyBranch = - await this.playerService.checkWhetherPlayerHasAllGroup( - game, - index, - userId - ); - this.playerService.checkFieldHasMaxBranches(fieldToBuyBranch); - const updatedGame = await this.playerService.buyBranch( - game, - fieldToBuyBranch, - socket.jwtPayload.sub - ); - const fields = await this.gameService.getGameFields(game.id); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: updatedGame }); - } - - @SubscribeMessage('sellBranch') - async sellBranch( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody('index') index: number - ) { - const game = socket.game; - const userId = socket.jwtPayload.sub; - const fields = await this.gameService.getGameFields(game.id); - const fieldToSellBranch = - await this.playerService.checkWhetherPlayerHasAllGroup( - game, - index, - userId, - false - ); - this.playerService.checkFieldHasBranches(fieldToSellBranch); - const updatedGame = await this.playerService.sellBranch( - game, - fieldToSellBranch, - socket.jwtPayload.sub - ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: updatedGame }); - } - - @SubscribeMessage('pledgeField') - async pledgeField( - @ConnectedSocket() - socket: Socket & { game: Partial }, - @MessageBody('index') index: number + @MessageBody() data: { index: number; requestId: string } ) { + const { index } = data; const game = socket.game; - const { player, fields } = await this.playerService.pledgeField( + const userId = socket.data.jwtPayload.sub; + const { player, fields } = await this.playerService.unmortgageField( game, - index - ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: player.game }); - } - - @UseGuards(TurnGuard) - @SubscribeMessage('payRedemptionForField') - async payRedemptionForField( - @ConnectedSocket() - socket: Socket & { game: Partial }, - @MessageBody('index') index: number - ) { - const game = socket.game; - const { player, fields } = await this.playerService.payRedemptionForField( - game, - index + index, + userId, + data.requestId ); this.server.to(game.id).emit('updateGameData', { fields, + requestId: data.requestId, game: player.game, }); } - - @UseGuards(TurnGuard) - @SubscribeMessage('offerTrade') - async offerTrade( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() - data: OfferTradeDto - ) { - const game = socket.game; - const userId = socket.jwtPayload.sub; - if (this.auctionService.getAuction(game.id)) - throw new WsException('Cannot offer trade while auction'); - await this.playerService.validateTradeData(game, data); - const trade = { ...data, fromUserId: userId } as Trade; - this.playerService.setTrade(game.id, trade); - const fromPlayer = game.players.find( - (player) => player.userId === trade.fromUserId - ); - const toPlayer = game.players.find( - (player) => player.userId === trade.toUserId - ); - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: `${fromPlayer.user.nickname} запропонував ${toPlayer.user.nickname} угоду!`, - chatId: game.chat.id, - }); - this.server.to(game.id).emit('gameChatMessage', message); - this.eventService.emitGameEvent('offerTrade', { game, trade }); - } - - @SubscribeMessage('refuseFromTrade') - async refuseFromTrade( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } - ) { - const game = socket.game; - this.playerService.refuseFromTrade(game); - } - - @SubscribeMessage('acceptTrade') - async acceptTrade( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } - ) { - const game = socket.game; - const trade = this.playerService.getTrade(game.id); - const { updatedGame, fields } = await this.playerService.acceptTrade( - game, - trade, - socket.jwtPayload.sub - ); - const data = { fields }; - - const { updatedGame: secondTimeUpdatedGame } = - await this.gameService.passTurnToUser({ - game: updatedGame, - toUserId: trade.fromUserId, - }); - if (secondTimeUpdatedGame) { - data['game'] = secondTimeUpdatedGame; - } - const fromPlayer = game.players.find( - (player) => player.userId === trade.fromUserId - ); - const toPlayer = game.players.find( - (player) => player.userId === trade.toUserId - ); - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: `Угода між ${fromPlayer.user.nickname} та ${toPlayer.user.nickname} підписана!`, - chatId: game.chat.id, - }); - this.server.to(game.id).emit('gameChatMessage', message); - this.server.to(game.id).emit('updateGameData', data); - } - - @SubscribeMessage('surrender') - async surrender( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } - ) { - const userId = socket.jwtPayload.sub; - const gameId = socket.game.id; - const fields = await this.gameService.getGameFields(socket.game.id); - const { updatedPlayer, updatedFields } = await this.playerService.loseGame( - userId, - gameId, - fields - ); - - this.server.to(gameId).emit('playerSurrendered', { - game: updatedPlayer.game, - fields: updatedFields, - }); - } } diff --git a/src/player/player.module.ts b/src/player/player.module.ts index 752b2a5..13f3f7e 100644 --- a/src/player/player.module.ts +++ b/src/player/player.module.ts @@ -1,34 +1,12 @@ import { forwardRef, Module } from '@nestjs/common'; -import { PlayerService } from './player.service'; +import { FieldModule } from 'src/field/field.module'; import { PlayerGateway } from './player.gateway'; import { PlayerRepository } from './player.repository'; -import { UserModule } from 'src/user/user.module'; +import { PlayerService } from './player.service'; import { JwtModule } from '@nestjs/jwt'; import { ConfigModule } from '@nestjs/config'; -import { GameModule } from 'src/game/game.module'; -import { EventModule } from 'src/event/event.module'; -import { WebSocketServerModule } from 'src/webSocketServer/webSocketServer.module'; -import { ChatModule } from 'src/chat/chat.module'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Field, FieldSchema } from 'src/schema/Field.schema'; -import { AuctionModule } from 'src/auction/auction.module'; @Module({ - imports: [ - forwardRef(() => GameModule), - UserModule, - JwtModule, - ConfigModule, - EventModule, - WebSocketServerModule, - ChatModule, - forwardRef(() => AuctionModule), - MongooseModule.forFeature([ - { - name: Field.name, - schema: FieldSchema, - }, - ]), - ], + imports: [FieldModule, JwtModule, ConfigModule], providers: [PlayerGateway, PlayerService, PlayerRepository], exports: [PlayerService], }) diff --git a/src/player/player.service.ts b/src/player/player.service.ts index 001b029..5d463f4 100644 --- a/src/player/player.service.ts +++ b/src/player/player.service.ts @@ -1,33 +1,19 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { CreatePlayerDto } from './dto/create-player.dto'; -import { PlayerPayload, PlayerRepository } from './player.repository'; +import { Injectable } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; import { Prisma } from '@prisma/client'; +import { FieldService } from 'src/field/field.service'; import { GamePayload } from 'src/game/game.repository'; -import { WsException } from '@nestjs/websockets'; -import { OfferTradeDto } from './dto/offer-trade.dto'; -import { Trade } from 'src/game/types/trade.type'; -import { GameService } from 'src/game/game.service'; -import { WebSocketServerService } from 'src/webSocketServer/webSocketServer.service'; -import { ChatService } from 'src/chat/chat.service'; -import { EventService } from 'src/event/event.service'; -import { Field, FieldDocument } from 'src/schema/Field.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; +import { FieldDocument } from 'src/schema/Field.schema'; +import { CreatePlayerDto } from './dto/create-player.dto'; +import { PlayerPayload, PlayerRepository } from './player.repository'; @Injectable() export class PlayerService { constructor( - @Inject(forwardRef(() => GameService)) - private readonly gameService: GameService, private playerRepository: PlayerRepository, - private webSocketServerService: WebSocketServerService, - private chatService: ChatService, - private eventService: EventService, - @InjectModel(Field.name) private fieldModel: Model - ) { - this.refuseFromTrade = this.refuseFromTrade.bind(this); - } - trades: Map = new Map(); + private fieldService: FieldService + ) {} + readonly COLORS = ['blue', 'yellow', 'green', 'purple', 'red']; create(createPlayerDto: CreatePlayerDto) { return this.playerRepository.create({ @@ -182,10 +168,6 @@ export class PlayerService { return player.money + potentialAmountToPledge; } - findPlayerFieldByIndex(fields: FieldDocument[], indexOfField: number) { - return fields.find((field) => field.index === indexOfField); - } - findPlayerWithTurn(game: Partial) { const player = game.players.find( (player) => player.userId === game.turnOfUserId @@ -193,19 +175,31 @@ export class PlayerService { return player; } + findPlayerWithUserId(game: Partial, userId: string) { + const player = game.players.find((player) => player.userId === userId); + return player; + } + async checkWhetherPlayerHasAllGroup( game: Partial, index: number, - userId?: string, - buying: boolean = true + userId: string, + buying: boolean = true, + requestId: string ) { - const fields = await this.gameService.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); const playerUserId = userId ? userId : game.turnOfUserId; const userFields = fields.filter((field) => field.ownedBy === playerUserId); const userFieldsIndexes = userFields.map((field) => field.index); if (!userFieldsIndexes.includes(index)) - throw new WsException('You dont have this field'); - const fieldToBuyBranch = this.findPlayerFieldByIndex(fields, index); + throw new WsException({ + message: 'You do not own this field', + requestId, + }); + const fieldToBuyBranch = this.fieldService.findPlayerFieldByIndex( + fields, + index + ); const groupFields = fields.filter( (f) => f.group === fieldToBuyBranch.group ); @@ -213,35 +207,56 @@ export class PlayerService { (f) => f.group === fieldToBuyBranch.group ); if (groupFields.length !== userGroupFields.length) - throw new WsException('You dont have all group fields'); + throw new WsException({ + message: 'You must own all fields in group to buy branches', + requestId, + }); const isBuyingHotel = buying && fieldToBuyBranch.amountOfBranches === 4; const isBuyingHouse = buying && fieldToBuyBranch.amountOfBranches < 4; const isSellingHotel = !buying && fieldToBuyBranch.amountOfBranches === 5; if (isBuyingHotel && game.hotelsQty <= 0) { - throw new WsException('You must have at least 1 hotel in the bank'); + throw new WsException({ + message: 'You must have at least 1 hotel in the bank', + requestId, + }); } if (isBuyingHouse && game.housesQty <= 0) { - throw new WsException('You must have at least 1 house in the bank'); + throw new WsException({ + message: 'You must have at least 1 house in the bank', + requestId, + }); } if (isSellingHotel && game.housesQty < 4) { - throw new WsException('You must have at least 4 houses in the bank'); + throw new WsException({ + message: 'There must be at least 4 houses in the bank to sell a hotel', + requestId, + }); } if (buying && fieldToBuyBranch.isPledged) { - throw new WsException('You cannot buy a branch for a pledged field'); + throw new WsException({ + message: 'You cant buy branches on a pledged field', + requestId, + }); } - this.checkBuyingOrSellingEvenly(groupFields, fieldToBuyBranch, buying); + this.checkBuyingOrSellingEvenly( + groupFields, + fieldToBuyBranch, + buying, + requestId + ); return fieldToBuyBranch; } checkBuyingOrSellingEvenly( groupOfFields: FieldDocument[], field: FieldDocument, - buying: boolean + buying: boolean, + requestId: string ) { const buyingEvenly = groupOfFields.every((groupField) => { const probableDifferenceOfBranches = Math.abs( @@ -253,91 +268,54 @@ export class PlayerService { ); }); if (!buyingEvenly) - throw new WsException('You must buy/sell branches evenly in group'); + throw new WsException({ + message: 'You must buy/sell branches evenly in group', + requestId, + }); } - checkFieldHasMaxBranches(field: FieldDocument) { + checkFieldHasMaxBranches(field: FieldDocument, requestId: string) { if (field.amountOfBranches >= 5) - throw new WsException('This field has max amount of branches'); + throw new WsException({ + message: 'This field has max amount of branches', + requestId, + }); } - checkFieldHasBranches(field: FieldDocument) { + checkFieldHasBranches(field: FieldDocument, requestId: string) { if (field.amountOfBranches >= 6) - throw new WsException('This field has max amount of branches'); + throw new WsException({ + message: 'This field has no branches to buy', + requestId, + }); if (field.amountOfBranches <= 0) { - throw new WsException('You do not have any branches to sell'); - } - } - - async buyBranch( - game: Partial, - fieldToBuyBranch: FieldDocument, - userId: string - ) { - const playerToPay = game.players.find((player) => player.userId === userId); - if (playerToPay.money < fieldToBuyBranch.branchPrice) { - throw new WsException('You dont have enough money to buy branch'); - } - const player = await this.decrementMoneyWithUserAndGameId( - userId, - game.id, - fieldToBuyBranch.branchPrice - ); - fieldToBuyBranch.amountOfBranches++; - let updatedGame = null; - if (fieldToBuyBranch.amountOfBranches === 5) { - await this.gameService.decreaseHotels(player.game.id, 1); - updatedGame = await this.gameService.increaseHouses(player.game.id, 4); - } else { - updatedGame = await this.gameService.decreaseHouses(player.game.id, 1); - } - await this.fieldModel.updateOne( - { _id: fieldToBuyBranch._id }, - { $set: { amountOfBranches: fieldToBuyBranch.amountOfBranches } } - ); - return updatedGame; - } - - async sellBranch( - game: Partial, - fieldToBuyBranch: FieldDocument, - userId: string - ) { - const player = await this.incrementMoneyWithUserAndGameId( - userId, - game.id, - fieldToBuyBranch.sellBranchPrice - ); - fieldToBuyBranch.amountOfBranches--; - let updatedGame = null; - if (fieldToBuyBranch.amountOfBranches === 4) { - await this.gameService.increaseHotels(player.game.id, 1); - updatedGame = await this.gameService.decreaseHouses(player.game.id, 4); - } else { - updatedGame = await this.gameService.increaseHouses(player.game.id, 1); + throw new WsException({ + message: 'This field has no branches to sell', + requestId, + }); } - await this.fieldModel.updateOne( - { _id: fieldToBuyBranch._id }, - { $set: { amountOfBranches: fieldToBuyBranch.amountOfBranches } } - ); - return updatedGame; } async pledgeField( game: Partial, index: number, - userId?: string + userId: string, + requestId: string ) { - const fields = await this.gameService.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); const playerUserId = userId ? userId : game.turnOfUserId; - const fieldToPledge = this.findPlayerFieldByIndex(fields, index); + const fieldToPledge = this.fieldService.findPlayerFieldByIndex( + fields, + index + ); if (fieldToPledge.isPledged) { - throw new WsException('Field is already pledged'); + throw new WsException({ message: 'Field is already pledged', requestId }); } if (fieldToPledge.amountOfBranches > 0) { - throw new WsException( - 'You must have some brances so you cant pledge the field' - ); + throw new WsException({ + message: 'You must sell all branches before pledging', + requestId, + }); } const player = await this.incrementMoneyWithUserAndGameId( playerUserId, @@ -346,159 +324,44 @@ export class PlayerService { ); fieldToPledge.isPledged = true; fieldToPledge.turnsToUnpledge = game.turnsToUnpledge; - await this.fieldModel.updateOne( - { _id: fieldToPledge._id }, - { $set: { isPledged: true, turnsToUnpledge: game.turnsToUnpledge } } - ); + await this.fieldService.updateById(fieldToPledge._id, { + isPledged: true, + turnsToUnpledge: game.turnsToUnpledge, + }); return { player, fields }; } - async payRedemptionForField( + async unmortgageField( game: Partial, index: number, - userId?: string + userId: string, + requestId: string ) { const playerUserId = userId ? userId : game.turnOfUserId; - const fields = await this.gameService.getGameFields(game.id); - const fieldToPayRedemption = this.findPlayerFieldByIndex(fields, index); - if (!fieldToPayRedemption.isPledged) { - throw new WsException('Field is not pledged'); - } - fieldToPayRedemption.isPledged = false; - fieldToPayRedemption.turnsToUnpledge = null; - await this.fieldModel.updateOne( - { _id: fieldToPayRedemption._id }, - { $set: { isPledged: false, turnsToUnpledge: null } } + const fields = await this.fieldService.getGameFields(game.id); + const fieldToUnmortgage = this.fieldService.findPlayerFieldByIndex( + fields, + index ); + if (!fieldToUnmortgage.isPledged) { + throw new WsException({ message: 'Field is not pledged', requestId }); + } + fieldToUnmortgage.isPledged = false; + fieldToUnmortgage.turnsToUnpledge = null; + await this.fieldService.updateById(fieldToUnmortgage._id, { + isPledged: false, + turnsToUnpledge: null, + }); const player = await this.decrementMoneyWithUserAndGameId( playerUserId, game.id, - fieldToPayRedemption.redemptionPrice + fieldToUnmortgage.redemptionPrice ); return { player, fields }; } - async validateTradeData(game: Partial, data: OfferTradeDto) { - if ( - data.offerFieldsIndexes.length === 0 && - data.wantedFieldsIndexes.length === 0 && - data.offeredMoney <= 0 && - data.wantedMoney <= 0 - ) { - throw new WsException('You must offer something'); - } - const fields = await this.gameService.getGameFields(game.id); - if (data.offerFieldsIndexes.length > 0) { - const userFields = fields.filter( - (field) => field.ownedBy === game.turnOfUserId - ); - const userFieldsIndexes = userFields.map((field) => field.index); - const hasAllOfferFields = data.offerFieldsIndexes.every((index) => - userFieldsIndexes.includes(index) - ); - if (!hasAllOfferFields) { - throw new WsException('You dont have all offer fields'); - } - } - if (data.wantedFieldsIndexes.length > 0) { - const otherUserFields = fields.filter( - (field) => field.ownedBy === data.toUserId - ); - const otherUserFieldsIndexes = otherUserFields.map( - (field) => field.index - ); - const hasAllWantedFields = data.wantedFieldsIndexes.every((index) => - otherUserFieldsIndexes.includes(index) - ); - if (!hasAllWantedFields) { - throw new WsException('Other player doesnt have all wanted fields'); - } - } - } - setTrade(gameId: string, trade: Trade) { - this.trades.set(gameId, trade); - } - getTrade(gameId: string) { - return this.trades.get(gameId); - } - async acceptTrade(game: Partial, trade: Trade, userId: string) { - if (!trade) throw new WsException('There is no trade to accept'); - if (trade.toUserId !== userId) - throw new WsException('You cant accept this trade'); - this.gameService.clearTimer(game.id); - const fields = await this.gameService.getGameFields(game.id); - if (trade.offerFieldsIndexes.length > 0) { - trade.offerFieldsIndexes.forEach((index) => { - const field = this.findPlayerFieldByIndex(fields, index); - field.ownedBy = trade.toUserId; - }); - } - if (trade.wantedFieldsIndexes.length > 0) { - trade.wantedFieldsIndexes.forEach((index) => { - const field = this.findPlayerFieldByIndex(fields, index); - field.ownedBy = trade.fromUserId; - }); - } - if (trade.offerFieldsIndexes.length || trade.wantedFieldsIndexes) { - await this.gameService.updateFields(fields, ['ownedBy']); - } - let player = null; - if (trade.offeredMoney) { - this.decrementMoneyWithUserAndGameId( - trade.fromUserId, - game.id, - trade.offeredMoney - ); - player = await this.incrementMoneyWithUserAndGameId( - trade.toUserId, - game.id, - trade.offeredMoney - ); - } - if (trade.wantedMoney) { - this.decrementMoneyWithUserAndGameId( - trade.toUserId, - game.id, - trade.wantedMoney - ); - player = await this.incrementMoneyWithUserAndGameId( - trade.fromUserId, - game.id, - trade.wantedMoney - ); - } - this.setTrade(game.id, null); - return { fields, updatedGame: player?.game ? player.game : game }; - } - - async refuseFromTrade(game: Partial) { - const trade = this.getTrade(game.id); - if (!trade) throw new WsException('There is no trade to refuse'); - this.setTrade(game.id, null); - - const { updatedGame } = await this.gameService.passTurnToUser({ - game, - toUserId: trade.fromUserId, - turnTime: game.timeOfTurn, - }); - this.webSocketServerService.server - .to(game.id) - .emit('updateGameData', { game: updatedGame }); - const toPlayer = game.players.find( - (player) => player.userId === trade.toUserId - ); - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: `${toPlayer.user.nickname} відхилив угоду!`, - chatId: game.chat.id, - }); - this.webSocketServerService.server - .to(game.id) - .emit('gameChatMessage', message); - } - - async loseGame(userId: string, gameId: string, fields: FieldDocument[]) { - this.gameService.clearTimer(gameId); - const updatedPlayer = await this.update({ + async updateLostGame(userId: string, gameId: string) { + return this.update({ where: { userId_gameId: { userId: userId, @@ -521,36 +384,21 @@ export class PlayerService { }, }, }); - const updatedGame = await this.gameService.updateById(gameId, { - dices: 'playerLost', - }); - fields.forEach((field) => { - if (field.ownedBy === updatedPlayer.userId) { - field.ownedBy = null; - field.amountOfBranches = 0; - field.isPledged = false; - field.turnsToUnpledge = null; - } - }); - await this.gameService.updateFields(fields, ['ownedBy']); - if (this.gameService.hasWinner(updatedPlayer.game)) { - this.gameService.clearTimer(updatedPlayer.game.id); - const game = await this.gameService.updateById(updatedPlayer.game.id, { - status: 'FINISHED', - }); - this.webSocketServerService.server - .to(game.id) - .emit('playerWon', { game }); - return { updatedPlayer, fields }; - } - this.eventService.emitGameEvent('passTurnToNext', { - game: updatedGame, - }); - return { updatedPlayer, updatedFields: fields }; } - validatePlayerMoney(player: Partial, moneyNeeded: number) { - if (!player) throw new WsException('No such player'); - if (player.money <= moneyNeeded) throw new WsException('Not enough money'); + validatePlayerMoney( + player: Partial, + moneyNeeded: number, + requestId: string + ) { + if (!player) + throw new WsException({ message: 'No such player', requestId }); + if (player.money <= moneyNeeded) + throw new WsException({ message: 'Not enough money', requestId }); + } + + choseRandomPlayer(players: Partial) { + const randomIndex = Math.floor(Math.random() * players.length); + return players[randomIndex]; } } diff --git a/src/secret/secret.module.ts b/src/secret/secret.module.ts new file mode 100644 index 0000000..cee860b --- /dev/null +++ b/src/secret/secret.module.ts @@ -0,0 +1,20 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { SecretService } from './secret.service'; +import { GameModule } from 'src/game/game.module'; +import { PlayerModule } from 'src/player/player.module'; +import { ChatModule } from 'src/chat/chat.module'; +import { FieldModule } from 'src/field/field.module'; +import { PaymentModule } from 'src/payment/payment.module'; + +@Module({ + imports: [ + forwardRef(() => GameModule), + PlayerModule, + ChatModule, + FieldModule, + forwardRef(() => PaymentModule), + ], + providers: [SecretService], + exports: [SecretService], +}) +export class SecretModule {} diff --git a/src/secret/secret.service.ts b/src/secret/secret.service.ts new file mode 100644 index 0000000..9867a5e --- /dev/null +++ b/src/secret/secret.service.ts @@ -0,0 +1,275 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; +import { Player } from '@prisma/client'; +import { ChatService } from 'src/chat/chat.service'; +import { FieldService } from 'src/field/field.service'; +import { GamePayload } from 'src/game/game.repository'; +import { GameService } from 'src/game/game.service'; +import { TwoPlayersInvolvedHandler } from 'src/game/handlers/twoPlayersInvolved.handler'; +import { SecretInfo } from 'src/game/types/secretInfo.type'; +import { PaymentService } from 'src/payment/payment.service'; +import { PlayerService } from 'src/player/player.service'; +import secretFields, { SecretType } from 'src/utils/fields/secretFields'; +import { SecretAnalyzer } from './secretAnalyzer'; +import { HandlerChain } from 'src/common/handlerChain'; +import { OnePlayerInvolvedHandler } from 'src/game/handlers/onePlayerInvolved.handler'; +import { AllPlayersInvolvedHandler } from 'src/game/handlers/allPlayersInvolved.handler'; + +@Injectable() +export class SecretService { + readonly secrets: Map = new Map(); + constructor( + private chatService: ChatService, + private fieldService: FieldService, + private playerService: PlayerService, + @Inject(forwardRef(() => PaymentService)) + private paymentService: PaymentService + ) {} + getRandomPlayersUserId(players: Partial) { + const randomIndex = Math.floor(Math.random() * players.length); + return players[randomIndex].userId; + } + choseRandomSecret() { + const randomSecretIndex = Math.floor(Math.random() * secretFields.length); + return secretFields[randomSecretIndex]; + } + + findIndexOfUserIdInSecretInfo(secretInfo: SecretInfo, userId: string) { + return secretInfo.users.findIndex( + (userIdInSecret) => userIdInSecret === userId + ); + } + + async parseAndSaveSecret(secret: SecretType, game: Partial) { + if (secret.numOfPlayersInvolved === 'one') { + const secretInfo = { + amounts: secret.amounts, + users: [game.turnOfUserId], + text: secret.text, + numOfPlayersInvolved: secret.numOfPlayersInvolved as 'one', + }; + this.secrets.set(game.id, secretInfo); + return secretInfo; + } else if (secret.numOfPlayersInvolved === 'two') { + const playersWithoutActive = game.players.filter( + (player) => player.userId !== game.turnOfUserId && !player.lost + ); + const randomUserId = this.getRandomPlayersUserId(playersWithoutActive); + const secretInfo = { + amounts: secret.amounts, + users: [randomUserId, game.turnOfUserId], + text: secret.text, + numOfPlayersInvolved: secret.numOfPlayersInvolved as 'two', + }; + this.secrets.set(game.id, secretInfo); + return secretInfo; + } else if (secret.numOfPlayersInvolved === 'all') { + const secretInfo = { + amounts: secret.amounts, + users: [ + game.turnOfUserId, + ...game.players + .filter((player) => player.userId !== game.turnOfUserId) + .map((player) => { + if (!player.lost && player.userId !== game.turnOfUserId) { + return player.userId; + } + return ''; + }), + ], + text: secret.text, + numOfPlayersInvolved: secret.numOfPlayersInvolved as 'all', + }; + this.secrets.set(game.id, secretInfo); + return secretInfo; + } + } + + async payToUserForSecret( + game: Partial, + userId: string, + requestId?: string + ) { + let secretInfo = this.secrets.get(game.id); + if (!secretInfo.users.includes(userId)) + throw new WsException({ + message: 'You cant pay for that secret', + requestId, + }); + const amount = secretInfo.amounts[1]; + if (amount > 0) + throw new WsException({ + message: 'You dont have to pay for this secret field', + requestId, + }); + const userToGetId = secretInfo.users[0]; + const indexOfUser = secretInfo.users.indexOf(userId); + const player = game.players.find((player) => player.userId === userId); + const fields = await this.fieldService.getGameFields(game.id); + let updatedPlayer = null; + let loseGame = false; + if (player.money < amount) { + loseGame = true; + updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( + userToGetId, + game.id, + this.playerService.estimateAssets(player, fields) + ); + } else { + updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( + userToGetId, + game.id, + amount + ); + await this.playerService.decrementMoneyWithUserAndGameId( + userId, + game.id, + amount + ); + } + secretInfo.users.splice(indexOfUser, 1, ''); + if ( + secretInfo.users.every((userId, index) => { + if (secretInfo.amounts[index] > 0) return true; + return userId === ''; + }) + ) { + secretInfo = null; + } + + return { game: updatedPlayer.game, secretInfo, loseGame }; + } + + async payAllforSecret(game: Partial) { + const secretInfo = this.secrets.get(game.id); + let updatedPlayer = null; + for (const userId of secretInfo.users) { + const firstUser = secretInfo.users[0]; + if (userId && userId !== firstUser) { + if (secretInfo.amounts.length === 2) { + updatedPlayer = await this.payToUserForSecret(game, userId); + } + + if (secretInfo.amounts.length === 1) { + const { playerWhoPayed } = await this.paymentService.transferWithBank( + game, + userId, + secretInfo.amounts[0] + ); + updatedPlayer = playerWhoPayed; + } + } + } + this.secrets.delete(game.id); + return updatedPlayer.game || game; + } + + async handleSecretWithMessage(game: Partial) { + const secret = this.choseRandomSecret(); + const secretInfo = await this.parseAndSaveSecret(secret, game); + if (secretInfo.text.includes('$RANDOM_PLAYER$')) { + const randomPlayer = this.playerService.choseRandomPlayer(game.players); + secretInfo.text = secretInfo.text.replace( + '$RANDOM_PLAYER$', + randomPlayer?.user.nickname + ); + } + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: secretInfo.text, + chatId: game.chat.id, + }); + return { message, secretInfo }; + } + + async resolveTwoUsers(game: Partial) { + let secretInfo = this.secrets.get(game.id); + const firstPay = secretInfo.amounts[0] < 1; + let updatedGameToReturn: null | Partial = null; + const fields = await this.fieldService.getGameFields(game.id); + if (firstPay) { + const userId = secretInfo.users[0]; + if (userId) { + const player = game.players.find((player) => player.userId === userId); + if ( + this.playerService.estimateAssets(player, fields) < + secretInfo.amounts[0] + ) { + return { loseGame: true, userId: player.userId, fields }; + } + const { updatedGame } = await this.paymentService.transferWithBank( + game, + userId, + secretInfo.amounts[0] + ); + updatedGameToReturn = updatedGame; + } + if (secretInfo.users[1]) { + const { updatedGame } = await this.paymentService.transferWithBank( + game, + secretInfo.users[1], + secretInfo.amounts[1] + ); + updatedGameToReturn = updatedGame; + } + } else { + const userId = secretInfo.users[1]; + if (userId) { + const player = game.players.find((player) => player.userId === userId); + if ( + this.playerService.estimateAssets(player, fields) < + secretInfo.amounts[1] + ) { + return { loseGame: true, userId: player.userId, fields }; + } + const { updatedGame } = await this.paymentService.transferWithBank( + game, + userId, + secretInfo.amounts[1] + ); + updatedGameToReturn = updatedGame; + } + if (secretInfo.users[0]) { + const { updatedGame } = await this.paymentService.transferWithBank( + game, + secretInfo.users[0], + secretInfo.amounts[0] + ); + updatedGameToReturn = updatedGame; + } + } + secretInfo = null; + return { + loseGame: false, + fields, + updatedGame: updatedGameToReturn, + }; + } + + async payToBankForSecret( + game: Partial, + userId: string, + requestId?: string + ) { + const secretInfo = this.secrets.get(game.id); + if (!secretInfo) + throw new WsException({ message: 'No secret found', requestId }); + if (!secretInfo.users.includes(userId)) { + throw new WsException({ + message: 'You cant pay to bank because no user in secretInfo', + requestId, + }); + } + const secretAnalyzer = new SecretAnalyzer(secretInfo, userId, requestId); + const chain = new HandlerChain(); + chain.addHandlers( + new OnePlayerInvolvedHandler(secretAnalyzer), + new TwoPlayersInvolvedHandler(secretAnalyzer, this), + new AllPlayersInvolvedHandler(secretAnalyzer) + ); + chain.process(); + const lastAmount = secretInfo.amounts[secretInfo.amounts.length - 1]; + const indexOfUser = this.findIndexOfUserIdInSecretInfo(secretInfo, userId); + secretInfo.users[indexOfUser] = ''; + return lastAmount; + } +} diff --git a/src/secret/secretAnalyzer.ts b/src/secret/secretAnalyzer.ts new file mode 100644 index 0000000..8643ed2 --- /dev/null +++ b/src/secret/secretAnalyzer.ts @@ -0,0 +1,30 @@ +import { SecretInfo } from 'src/game/types/secretInfo.type'; + +export class SecretAnalyzer { + constructor( + public readonly secretInfo: SecretInfo, + public readonly userId: string, + public readonly requestId?: string + ) {} + isOneUserHaveToPay() { + return this.secretInfo.users.length === 1 && this.secretInfo.amounts[0] < 0; + } + isOneUserHaveToReceive() { + return this.secretInfo.users.length === 1 && this.secretInfo.amounts[0] > 0; + } + inOnePlayerInvolved() { + return this.secretInfo.numOfPlayersInvolved === 'one'; + } + isTwoPlayersInvolved() { + return this.secretInfo.numOfPlayersInvolved === 'two'; + } + isAllPlayersInvolved() { + return this.secretInfo.numOfPlayersInvolved === 'all'; + } + isFirstAndShouldGet() { + return ( + this.secretInfo.users[0] === this.userId && + this.secretInfo.amounts[0] === null + ); + } +} diff --git a/src/sse/sse.module.ts b/src/sse/sse.module.ts new file mode 100644 index 0000000..81848a6 --- /dev/null +++ b/src/sse/sse.module.ts @@ -0,0 +1,5 @@ +import { Module } from '@nestjs/common'; +import { SseService } from './sse.service'; + +@Module({ providers: [SseService], exports: [SseService] }) +export class SseModule {} diff --git a/src/sse/sse.service.ts b/src/sse/sse.service.ts new file mode 100644 index 0000000..20f9253 --- /dev/null +++ b/src/sse/sse.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@nestjs/common'; +import { Subject, Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; + +interface SseEvent { + room?: string; + userId?: string; + data: any; +} + +@Injectable() +export class SseService { + private eventSubject = new Subject(); + onModuleInit() { + setInterval(() => { + this.sendToAll({ + data: { hello: 'world', time: new Date().toISOString() }, + }); + }, 2000); + } + sendToRoom(room: string, data: any) { + this.eventSubject.next({ room, data }); + } + + sendToUser(userId: string, data: any) { + this.eventSubject.next({ userId, data }); + } + + sendToAll(data: any) { + this.eventSubject.next({ data }); + } + + getEventStream(room?: string, userId?: string): Observable { + return this.eventSubject.asObservable().pipe( + filter((event) => { + if (room && event.room !== room) return false; + if (userId && event.userId !== userId) return false; + return true; + }), + ); + } +} diff --git a/src/timer/timers.module.ts b/src/timer/timers.module.ts new file mode 100644 index 0000000..736b592 --- /dev/null +++ b/src/timer/timers.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { TimerService } from './timers.service'; + +@Module({ + providers: [TimerService], + exports: [TimerService], +}) +export class TimerModule {} diff --git a/src/timer/timers.service.ts b/src/timer/timers.service.ts new file mode 100644 index 0000000..31d204a --- /dev/null +++ b/src/timer/timers.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Logger } from '@nestjs/common'; + +@Injectable() +export class TimerService { + private readonly logger = new Logger(TimerService.name); + constructor() {} + timers: Map< + string, + { timer: NodeJS.Timeout; reject: (message: string) => void } + > = new Map(); + set( + id: string, + time: number, + args: T, + callback: (args: T) => Promise | R + ): Promise | null { + this.clear(id); + return new Promise((resolve, reject) => { + const timer = setTimeout(async () => { + try { + const res: R = await callback(args); + resolve(res); + } catch (err: unknown) { + const error = err instanceof Error ? err : new Error(String(err)); + console.log('In Timer'); + console.log(error); + reject(error); + } + }, time); + this.timers.set(id, { timer, reject }); + this.logger.log(`Timer with id:${id} was set for ${time / 1000} seconds`); + }).catch(() => { + return null; + }); + } + clear(gameId: string) { + if (this.timers.has(gameId)) { + const timer = this.timers.get(gameId); + clearTimeout(timer.timer); + this.timers.delete(gameId); + this.logger.log(`Cleared timer for game ${gameId}.`); + } + } + + calculateFutureTime(duration: number) { + let currentTime = Date.now(); + currentTime += duration; + return currentTime.toString(); + } +} diff --git a/src/player/dto/offer-trade.dto.ts b/src/trade/dto/offer-trade.dto.ts similarity index 100% rename from src/player/dto/offer-trade.dto.ts rename to src/trade/dto/offer-trade.dto.ts diff --git a/src/trade/trade.gateway.ts b/src/trade/trade.gateway.ts new file mode 100644 index 0000000..f35282c --- /dev/null +++ b/src/trade/trade.gateway.ts @@ -0,0 +1,125 @@ +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; +import { + ConnectedSocket, + MessageBody, + SubscribeMessage, + WebSocketGateway, + WsException, +} from '@nestjs/websockets'; +import { Socket } from 'socket.io'; +import { ValidPlayerGuard, TurnGuard } from 'src/auth/guard'; +import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; +import { GamePayload } from 'src/game/game.repository'; +import { OfferTradeDto } from './dto/offer-trade.dto'; +import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; +import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; +import { AuctionService } from 'src/auction/auction.service'; +import { TradeService } from './trade.service'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; +import { ChatService } from 'src/chat/chat.service'; +import { Trade } from 'src/game/types/trade.type'; + +@WebSocketGateway({ + cors: { + origin: + process.env.NODE_ENV === 'development' + ? process.env.FRONTEND_URL_DEV + : process.env.FRONTEND_URL_PROD, + methods: ['GET', 'POST'], + credentials: true, + }, +}) +@UseFilters(WebsocketExceptionsFilter) +@UsePipes(new WsValidationPipe()) +export class TradeGateway { + constructor( + private auctionService: AuctionService, + private tradeService: TradeService, + private webSocketProvider: WebSocketProvider, + private chatService: ChatService + ) {} + @UseGuards(ValidPlayerGuard, TurnGuard) + @SubscribeMessage('offerTrade') + async offerTrade( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() + data: { tradeOffer: OfferTradeDto; requestId: string } + ) { + const game = socket.game; + const userId = socket.data.jwtPayload.sub; + if (this.auctionService.getAuction(game.id)) + throw new WsException({ + message: 'Cannot offer trade while auction', + requestId: data.requestId, + }); + await this.tradeService.validateTradeData( + game, + data.tradeOffer, + data.requestId + ); + const trade = { ...data.tradeOffer, fromUserId: userId } as Trade; + this.tradeService.setTrade(game.id, trade); + const fromPlayer = game.players.find( + (player) => player.userId === trade.fromUserId + ); + const toPlayer = game.players.find( + (player) => player.userId === trade.toUserId + ); + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: `${fromPlayer.user.nickname} запропонував ${toPlayer.user.nickname} угоду!`, + chatId: game.chat.id, + }); + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.tradeService.handleOfferTrade({ + game, + trade, + requestId: data.requestId, + }); + } + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('refuseFromTrade') + async refuseFromTrade( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { requestId: string } + ) { + const game = socket.game; + this.tradeService.refuseFromTrade(game, data.requestId); + } + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('acceptTrade') + async acceptTrade( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { requestId: string } + ) { + const game = socket.game; + const trade = this.tradeService.getTrade(game.id); + const { updatedGame, fields } = await this.tradeService.acceptTrade( + game, + trade, + socket.data.jwtPayload.sub, + data.requestId + ); + const gameData = { fields }; + gameData['requestId'] = data.requestId; + if (updatedGame) { + gameData['game'] = updatedGame; + } + const fromPlayer = game.players.find( + (player) => player.userId === trade.fromUserId + ); + const toPlayer = game.players.find( + (player) => player.userId === trade.toUserId + ); + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: `Угода між ${fromPlayer.user.nickname} та ${toPlayer.user.nickname} підписана!`, + chatId: game.chat.id, + }); + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.webSocketProvider.server.to(game.id).emit('updateGameData', gameData); + } +} diff --git a/src/trade/trade.module.ts b/src/trade/trade.module.ts new file mode 100644 index 0000000..4f35460 --- /dev/null +++ b/src/trade/trade.module.ts @@ -0,0 +1,28 @@ +import { Module } from '@nestjs/common'; +import { TradeService } from './trade.service'; +import { TradeGateway } from './trade.gateway'; +import { GameModule } from 'src/game/game.module'; +import { WebSocketProviderModule } from 'src/webSocketProvider/webSocketProvider.module'; +import { FieldModule } from 'src/field/field.module'; +import { ChatModule } from 'src/chat/chat.module'; +import { TimerModule } from 'src/timer/timers.module'; +import { PlayerModule } from 'src/player/player.module'; +import { AuctionModule } from 'src/auction/auction.module'; +import { JwtModule } from '@nestjs/jwt'; +import { ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [ + GameModule, + WebSocketProviderModule, + FieldModule, + ChatModule, + TimerModule, + PlayerModule, + AuctionModule, + JwtModule, + ConfigModule, + ], + providers: [TradeService, TradeGateway], +}) +export class TradeModule {} diff --git a/src/trade/trade.service.ts b/src/trade/trade.service.ts new file mode 100644 index 0000000..c216884 --- /dev/null +++ b/src/trade/trade.service.ts @@ -0,0 +1,198 @@ +import { Injectable } from '@nestjs/common'; +import { GamePayload } from 'src/game/game.repository'; +import { GameService } from 'src/game/game.service'; +import { Trade } from 'src/game/types/trade.type'; +import { OfferTradeDto } from './dto/offer-trade.dto'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; +import { FieldService } from 'src/field/field.service'; +import { ChatService } from 'src/chat/chat.service'; +import { TimerService } from 'src/timer/timers.service'; +import { WsException } from '@nestjs/websockets'; +import { PlayerService } from 'src/player/player.service'; + +@Injectable() +export class TradeService { + trades: Map = new Map(); + constructor( + private gameService: GameService, + private webSocketProvider: WebSocketProvider, + private fieldService: FieldService, + private chatService: ChatService, + private timerService: TimerService, + private playerService: PlayerService + ) { + this.refuseFromTrade = this.refuseFromTrade.bind(this); + } + setTrade(gameId: string, trade: Trade) { + this.trades.set(gameId, trade); + } + getTrade(gameId: string) { + return this.trades.get(gameId); + } + + async validateTradeData( + game: Partial, + data: OfferTradeDto, + requestId: string + ) { + if ( + data.offerFieldsIndexes.length === 0 && + data.wantedFieldsIndexes.length === 0 && + data.offeredMoney <= 0 && + data.wantedMoney <= 0 + ) { + throw new WsException({ message: 'Invalid trade data', requestId }); + } + const fields = await this.fieldService.getGameFields(game.id); + if (data.offerFieldsIndexes.length > 0) { + const userFields = fields.filter( + (field) => field.ownedBy === game.turnOfUserId + ); + const userFieldsIndexes = userFields.map((field) => field.index); + const hasAllOfferFields = data.offerFieldsIndexes.every((index) => + userFieldsIndexes.includes(index) + ); + if (!hasAllOfferFields) { + throw new WsException({ + message: 'You dont have all offer fields', + requestId, + }); + } + } + if (data.wantedFieldsIndexes.length > 0) { + const otherUserFields = fields.filter( + (field) => field.ownedBy === data.toUserId + ); + const otherUserFieldsIndexes = otherUserFields.map( + (field) => field.index + ); + const hasAllWantedFields = data.wantedFieldsIndexes.every((index) => + otherUserFieldsIndexes.includes(index) + ); + if (!hasAllWantedFields) { + throw new WsException({ + message: 'The other player does not have all wanted fields', + requestId, + }); + } + } + } + + async handleOfferTrade(data: { + game: Partial; + trade: Trade; + requestId: string; + }) { + const { updatedGame } = await this.gameService.passTurnToUser({ + game: data.game, + toUserId: data.trade.toUserId, + }); + this.webSocketProvider.server + .to(data.game.id) + .emit('updateGameData', { game: updatedGame, requestId: data.requestId }); + this.webSocketProvider.server + .to(data.trade.toUserId) + .emit('tradeOffered', { trade: data.trade, requestId: data.requestId }); + this.timerService.set( + updatedGame.id, + 10000, + updatedGame, + this.refuseFromTrade + ); + } + + async acceptTrade( + game: Partial, + trade: Trade, + userId: string, + requestId: string + ) { + if (!trade) + throw new WsException({ + message: 'There is no trade to accept', + requestId, + }); + if (trade.toUserId !== userId) + throw new WsException({ + message: 'You are not allowed to accept this trade', + requestId, + }); + this.timerService.clear(game.id); + const fields = await this.fieldService.getGameFields(game.id); + if (trade.offerFieldsIndexes.length > 0) { + trade.offerFieldsIndexes.forEach((index) => { + const field = this.fieldService.findPlayerFieldByIndex(fields, index); + field.ownedBy = trade.toUserId; + }); + } + if (trade.wantedFieldsIndexes.length > 0) { + trade.wantedFieldsIndexes.forEach((index) => { + const field = this.fieldService.findPlayerFieldByIndex(fields, index); + field.ownedBy = trade.fromUserId; + }); + } + if (trade.offerFieldsIndexes.length || trade.wantedFieldsIndexes) { + await this.fieldService.updateFields(fields, ['ownedBy']); + } + let player = null; + if (trade.offeredMoney) { + this.playerService.decrementMoneyWithUserAndGameId( + trade.fromUserId, + game.id, + trade.offeredMoney + ); + player = await this.playerService.incrementMoneyWithUserAndGameId( + trade.toUserId, + game.id, + trade.offeredMoney + ); + } + if (trade.wantedMoney) { + this.playerService.decrementMoneyWithUserAndGameId( + trade.toUserId, + game.id, + trade.wantedMoney + ); + player = await this.playerService.incrementMoneyWithUserAndGameId( + trade.fromUserId, + game.id, + trade.wantedMoney + ); + } + this.setTrade(game.id, null); + const { updatedGame } = await this.gameService.passTurnToUser({ + game: player?.game ? player.game : game, + toUserId: trade.fromUserId, + }); + return { fields, updatedGame }; + } + + async refuseFromTrade(game: Partial, requestId?: string) { + const trade = this.getTrade(game.id); + if (!trade) + throw new WsException({ + message: 'There is no trade to refuse', + requestId, + }); + this.setTrade(game.id, null); + + const { updatedGame } = await this.gameService.passTurnToUser({ + game, + toUserId: trade.fromUserId, + turnTime: game.timeOfTurn, + }); + this.webSocketProvider.server + .to(game.id) + .emit('updateGameData', { game: updatedGame }); + const toPlayer = game.players.find( + (player) => player.userId === trade.toUserId + ); + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: `${toPlayer.user.nickname} відхилив угоду!`, + chatId: game.chat.id, + }); + this.webSocketProvider.server + .to(game.id) + .emit('gameChatMessage', { requestId, ...message }); + } +} diff --git a/src/utils/exceptions/websocket-exceptions.filter.ts b/src/utils/exceptions/websocket-exceptions.filter.ts index b1c8d24..d1f1d8a 100644 --- a/src/utils/exceptions/websocket-exceptions.filter.ts +++ b/src/utils/exceptions/websocket-exceptions.filter.ts @@ -9,6 +9,6 @@ export class WebsocketExceptionsFilter implements WsExceptionFilter { const client: Socket = ctx.getClient(); const error = exception.getError(); const details = typeof error === 'string' ? { message: error } : error; - client.emit('error', { status: 'error', ...details }); + client.emit('error', { type: 'error', ...details }); } } diff --git a/src/utils/fields/secretFields.ts b/src/utils/fields/secretFields.ts index 2aa7a62..086e928 100644 --- a/src/utils/fields/secretFields.ts +++ b/src/utils/fields/secretFields.ts @@ -6,7 +6,7 @@ const secretFields = [ }, { text: '$RANDOM_PLAYER$ reported him/her to NABU for undeclared property that he/she did not list in financial documents. Pays a fine of 1500mm. The reporter receives a reward of 3500mm.', - amounts: [-1500, 3500], + amounts: [3500, -1500], numOfPlayersInvolved: 'two', }, { diff --git a/src/webSocketProvider/webSocketProvider.module.ts b/src/webSocketProvider/webSocketProvider.module.ts new file mode 100644 index 0000000..e9d52e8 --- /dev/null +++ b/src/webSocketProvider/webSocketProvider.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { WebSocketProvider } from './webSocketProvider.service'; + +@Module({ + providers: [WebSocketProvider], + exports: [WebSocketProvider], +}) +export class WebSocketProviderModule {} diff --git a/src/webSocketProvider/webSocketProvider.service.ts b/src/webSocketProvider/webSocketProvider.service.ts new file mode 100644 index 0000000..2070d5e --- /dev/null +++ b/src/webSocketProvider/webSocketProvider.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@nestjs/common'; +import { Server } from 'socket.io'; + +@Injectable() +export class WebSocketProvider { + private _server: Server | null = null; + + setServer(server: Server) { + this._server = server; + } + + get server(): Server { + if (!this._server) { + throw new Error('WebSocket server is not initialized'); + } + return this._server; + } +} diff --git a/src/webSocketServer/webSocketServer.module.ts b/src/webSocketServer/webSocketServer.module.ts deleted file mode 100644 index e04a3f8..0000000 --- a/src/webSocketServer/webSocketServer.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { WebSocketServerService } from './webSocketServer.service'; - -@Module({ - providers: [WebSocketServerService], - exports: [WebSocketServerService], -}) -export class WebSocketServerModule {} diff --git a/src/webSocketServer/webSocketServer.service.ts b/src/webSocketServer/webSocketServer.service.ts deleted file mode 100644 index b864ecb..0000000 --- a/src/webSocketServer/webSocketServer.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Server } from 'socket.io'; - -@Injectable() -export class WebSocketServerService { - public server: Server; - - setServer(server: Server) { - this.server = server; - } -}