diff --git a/.gitignore b/.gitignore index 2309cc8..2fb9a0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# ---> Node +### Node ### # Logs logs *.log @@ -79,7 +79,7 @@ web_modules/ .env.test.local .env.production.local .env.local - +.env.development.env # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache @@ -103,13 +103,6 @@ dist # vuepress v2.x temp and cache directory .temp -.cache - -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache # Docusaurus cache and generated files .docusaurus @@ -136,3 +129,18 @@ dist .yarn/install-state.gz .pnp.* +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +# End of https://www.toptal.com/developers/gitignore/api/node + +/docker +.development.env +*.env +/files diff --git a/build_n_deploy.sh b/build_n_deploy.sh new file mode 100644 index 0000000..1582d26 --- /dev/null +++ b/build_n_deploy.sh @@ -0,0 +1,13 @@ +git pull + +docker compose -f dev.docker-compose.yml up -d --build + +docker run --rm \ + -e SONAR_HOST_URL="https://sq.ittalie.ir" \ + -e SONAR_TOKEN="sqp_42fe574fde1e2d527813f4fb6912ad0040c6850a" \ + -v "$(pwd):/usr/src" \ + sonarsource/sonar-scanner-cli \ + -Dsonar.scm.provider=git \ + -Dsonar.projectKey=yara724-main-api \ + -Dsonar.sources=. + diff --git a/dev.Dockerfile b/dev.Dockerfile new file mode 100644 index 0000000..7b73c3e --- /dev/null +++ b/dev.Dockerfile @@ -0,0 +1,20 @@ +# Use the official Node.js image as the base image +FROM node:20-alpine + +# Set the working directory inside the container +WORKDIR /app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install the project dependencies +RUN npm i --force + +# Copy the rest of the application code to the working directory +COPY . . + +# Expose the port on which your NestJS application will run +# EXPOSE 9000 + +# Start the NestJS application +CMD ["npm", "start"] diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml new file mode 100644 index 0000000..b88fd2e --- /dev/null +++ b/dev.docker-compose.yml @@ -0,0 +1,35 @@ +version: '3.7' + +services: + api: + build: + context: . + dockerfile: dev.Dockerfile + container_name: yara724-main-api-dev + image: yara724-main-api:dev + restart: unless-stopped + volumes: + - yara724-main-api-dev-files:/app/files/:rw + env_file: + - .development.env + #environment: + # - "MONGODB_USERNAME=ittalie_dev" + # - "MONGODB_PASSWORD=t3hYwwj9jbu8QB6aMxt8KLnj3qEQDuMM" + # - "MONGODB_HOSTNAME=db.ittalie.ir" + # - "MONGODB_PORT=3082" + # - "NODE_ENV=development" + # - "PORT=3002" + # - "SWAGGER_USER=admin" + # - "SWAGGER_PASSWORD=123321" + # - "SECRET=h43h$24qaw849f3912d@3as" + ports: + - 3080:3002 +networks: + default: + driver: bridge + ipam: + config: + - subnet: 172.16.58.0/24 +volumes: + yara724-main-api-dev-files: + name: yara724-main-api-dev-files diff --git a/err.json b/err.json new file mode 100644 index 0000000..90c16b7 --- /dev/null +++ b/err.json @@ -0,0 +1 @@ +{"1000":{"info":"start","message":"start"},"1001":{"info":"Access Denied Other Actor Lock File","message":""},"1004":{"info":"g","message":""},"1005":{"info":"fSF","message":""},"1006":{"info":"request not found ","message":""}} \ No newline at end of file diff --git a/nest-cli.json b/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5d4602c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,19051 @@ +{ + "name": "yara724", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "yara724", + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@arashioz/errjson-talieh": "^2.2.5", + "@fraybabak/kavenegar_nest": "^1.0.5", + "@nestjs-modules/mailer": "^2.0.2", + "@nestjs/axios": "^3.1.3", + "@nestjs/common": "^10.4.15", + "@nestjs/core": "^10.4.15", + "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "*", + "@nestjs/mongoose": "^10.1.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-express": "^10.4.15", + "@nestjs/platform-fastify": "^10.4.15", + "@nestjs/platform-socket.io": "^10.4.15", + "@nestjs/schedule": "^4.1.2", + "@nestjs/serve-static": "^5.0.3", + "@nestjs/swagger": "^8.1.0", + "@nestjs/websockets": "^10.4.15", + "@types/uuid": "^10.0.0", + "axios": "^1.9.0", + "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "crypto": "^1.0.1", + "dotenv": "^16.4.7", + "express-basic-auth": "^1.2.1", + "fastest-levenshtein": "^1.0.16", + "form-data": "^4.0.2", + "jalali-moment": "^3.3.11", + "kavenegar": "^1.1.4", + "mongoose": "^8.9.2", + "nestjs-command": "^3.1.4", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "short-unique-id": "^5.2.0", + "standard": "^17.1.2", + "standardjs": "^1.0.0-alpha", + "uuid": "^11.0.3", + "yargs": "^17.7.2" + }, + "devDependencies": { + "@nestjs/cli": "^10.4.9", + "@nestjs/schematics": "^10.2.3", + "@nestjs/testing": "^10.4.15", + "@types/express": "^5.0.0", + "@types/jest": "^29.5.14", + "@types/multer": "^1.4.12", + "@types/node": "^22.10.2", + "@types/passport-jwt": "^4.0.1", + "@types/supertest": "^6.0.2", + "@types/yargs": "^17.0.33", + "@typescript-eslint/eslint-plugin": "^8.18.1", + "@typescript-eslint/parser": "^8.18.1", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "jest": "^29.7.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "ts-standard": "^12.0.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.2" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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, + "license": "MIT", + "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/core/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core/node_modules/picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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, + "license": "MIT", + "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": { + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-17.3.11.tgz", + "integrity": "sha512-kcOMqp+PHAKkqRad7Zd7PbpqJ0LqLaNZdY1+k66lLWmkEBozgq8v4ASn/puPWf9Bo0HpCiK+EzLf0VHE8Z/y6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "17.3.11", + "@angular-devkit/schematics": "17.3.11", + "ansi-colors": "4.1.3", + "inquirer": "9.2.15", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "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", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/inquirer": { + "version": "9.2.15", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", + "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/@arashioz/errjson-talieh": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@arashioz/errjson-talieh/-/errjson-talieh-2.2.5.tgz", + "integrity": "sha512-SdrPtxKRBKng5OjPoyBrI1kNOyvKx62aaPGA20cElxvFPzy9k9l6l9X8AeGcHddLKgC26NN6rqaW4agzMQ4p/Q==", + "license": "ISC", + "dependencies": { + "@babel/cli": "^7.23.4", + "@babel/core": "^7.23.5", + "@babel/preset-env": "^7.23.5", + "@babel/preset-typescript": "^7.23.3", + "typescript": "^5.0.0", + "undici-types": "^5.26.5" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@babel/cli": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.4.tgz", + "integrity": "sha512-j3luA9xGKCXVyCa5R7lJvOMM+Kc2JEnAEIgz2ggtjQ/j5YUVgfsg/WsG95bbsgq7YLHuiCOzMnoSasuY16qiCw==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "commander": "^4.0.1", + "convert-source-map": "^2.0.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/cli/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/cli/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@babel/cli/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", + "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", + "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", + "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", + "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", + "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "dependencies": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", + "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.4", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.5", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "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==", + "license": "MIT", + "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" + ], + "license": "MIT", + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "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==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.10.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.2.tgz", + "integrity": "sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fastify/cors": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", + "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==", + "license": "MIT", + "dependencies": { + "fastify-plugin": "^4.0.0", + "mnemonist": "0.39.6" + } + }, + "node_modules/@fastify/error": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==", + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/formbody": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-7.4.0.tgz", + "integrity": "sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==", + "dependencies": { + "fast-querystring": "^1.0.0", + "fastify-plugin": "^4.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", + "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/middie": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@fastify/middie/-/middie-8.3.3.tgz", + "integrity": "sha512-+WHavMQr9CNTZoy2cjoDxoWp76kZ3JKjAtZj5sXNlxX5XBzHig0TeCPfPc+1+NQmliXtndT3PFwAjrQHE/6wnQ==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^3.2.0", + "fastify-plugin": "^4.0.0", + "path-to-regexp": "^6.3.0", + "reusify": "^1.0.4" + } + }, + "node_modules/@fastify/middie/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", + "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@fastify/proxy-addr/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@fraybabak/kavenegar_nest": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@fraybabak/kavenegar_nest/-/kavenegar_nest-1.0.5.tgz", + "integrity": "sha512-1zU6XaYsVQLFsUJC9Z+K/8LU4UkSc//WmbWZkJAri0aZQtZ/ide2AJfIs7grO4wiZ3r1OHppBYcTQyMnh+0x5Q==", + "dependencies": { + "@nestjs/common": "^8.0.9", + "@nestjs/core": "^8.0.9", + "rxjs": "^7.3.1" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/@nestjs/common": { + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.7.tgz", + "integrity": "sha512-m/YsbcBal+gA5CFrDpqXqsSfylo+DIQrkFY3qhVIltsYRfu8ct8J9pqsTO6OPf3mvqdOpFGrV5sBjoyAzOBvsw==", + "dependencies": { + "axios": "0.27.2", + "iterare": "1.2.1", + "tslib": "2.4.0", + "uuid": "8.3.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "cache-manager": "*", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "cache-manager": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/@nestjs/core": { + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.4.7.tgz", + "integrity": "sha512-XB9uexHqzr2xkPo6QSiQWJJttyYYLmvQ5My64cFvWFi7Wk2NIus0/xUNInwX3kmFWB6pF1ab5Y2ZBvWdPwGBhw==", + "hasInstallScript": true, + "dependencies": { + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "object-hash": "3.0.0", + "path-to-regexp": "3.2.0", + "tslib": "2.4.0", + "uuid": "8.3.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0", + "@nestjs/microservices": "^8.0.0", + "@nestjs/platform-express": "^8.0.0", + "@nestjs/websockets": "^8.0.0", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/@nestjs/platform-express": { + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.4.7.tgz", + "integrity": "sha512-lPE5Ltg2NbQGRQIwXWY+4cNrXhJdycbxFDQ8mNxSIuv+LbrJBIdEB/NONk+LLn9N/8d2+I2LsIETGQrPvsejBg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "body-parser": "1.20.0", + "cors": "2.8.5", + "express": "4.18.1", + "multer": "1.4.4-lts.1", + "tslib": "2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0", + "@nestjs/core": "^8.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/@nestjs/platform-socket.io": { + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-8.4.7.tgz", + "integrity": "sha512-XvLfl2BjvKylBMdyNlOrWcT+qWhMk3KN8spRWMMeP/OW+s8PlWHDFSe/BWOI/C9+tZtdqbzNDqYnnci4vv4tOg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "socket.io": "4.5.1", + "tslib": "2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0", + "@nestjs/websockets": "^8.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/@nestjs/websockets": { + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-8.4.7.tgz", + "integrity": "sha512-UeXKTR7s2vQTGsSFhFR1dunptiICNf24nkLWoBud0kKx8HCRnhsNycyXbtwtAkioTjYXqm+vWeb9eb1Nv6+r2w==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.4.0" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0", + "@nestjs/core": "^8.0.0", + "@nestjs/platform-socket.io": "^8.0.0", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/engine.io": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "license": "MIT", + "optional": true, + "peer": true, + "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", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/engine.io-parser": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz", + "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/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==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/socket.io": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", + "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/socket.io-parser": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", + "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@fraybabak/kavenegar_nest/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "license": "BSD-3-Clause" + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", + "license": "MIT" + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "@css-inline/css-inline": "0.14.1", + "glob": "10.3.12" + }, + "optionalDependencies": { + "@types/ejs": "^3.1.5", + "@types/mjml": "^4.7.4", + "@types/pug": "^2.0.10", + "ejs": "^3.1.10", + "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/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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==", + "license": "ISC", + "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/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs/axios": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.1.3.tgz", + "integrity": "sha512-RZ/63c1tMxGLqyG3iOCVt7A72oy4x1eM6QEhd4KzCYpaVWW0igq0WSREeRoEZhIxRcZfDfIIkvsOMiM7yfVGZQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "axios": "^1.3.1", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/@nestjs/cli": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz", + "integrity": "sha512-s8qYd97bggqeK7Op3iD49X2MpFtW4LVNLAwXFkfbRxKME6IYT7X0muNTJ2+QfI8hpbNx9isWkrLWIp+g5FOhiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "17.3.11", + "@angular-devkit/schematics": "17.3.11", + "@angular-devkit/schematics-cli": "17.3.11", + "@nestjs/schematics": "^10.0.1", + "chalk": "4.1.2", + "chokidar": "3.6.0", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.0.2", + "glob": "10.4.5", + "inquirer": "8.2.6", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tree-kill": "1.2.2", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.7.2", + "webpack": "5.97.1", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 16.14" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.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, + "license": "ISC", + "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/@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, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@nestjs/cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@nestjs/common": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.15.tgz", + "integrity": "sha512-vaLg1ZgwhG29BuLDxPA9OAcIlgqzp9/N8iG0wGapyUNTf4IY4O6zAHgN6QalwLhFxq7nOI021vdRojR1oF3bqg==", + "license": "MIT", + "dependencies": { + "iterare": "1.2.1", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/core": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.15.tgz", + "integrity": "sha512-UBejmdiYwaH6fTsz2QFBlC1cJHM+3UDeLZN+CiP9I1fRv2KlBZsmozGLbV5eS1JAVWJB4T5N5yQ0gjN8ZvcS2w==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "3.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@nestjs/websockets": "^10.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/core/node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/@nestjs/mapped-types": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz", + "integrity": "sha512-84ze+CPfp1OWdpRi1/lOu59hOhTz38eVzJvRKrg9ykRFwDz+XleKfMsG0gUqNZYFa6v53XYzeD+xItt8uDW7NQ==", + "license": "MIT", + "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/mongoose": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.1.0.tgz", + "integrity": "sha512-1ExAnZUfh2QffEaGjqYGgVPy/sYBQCVLCLqVgkcClKx/BCd0QNgND8MB70lwyobp3nm/+nbGQqBpu9F3/hgOCw==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "mongoose": "^6.0.2 || ^7.0.0 || ^8.0.0", + "rxjs": "^7.0.0" + } + }, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, + "node_modules/@nestjs/platform-express": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.15.tgz", + "integrity": "sha512-63ZZPkXHjoDyO7ahGOVcybZCRa7/Scp6mObQKjcX/fTEq1YJeU75ELvMsuQgc8U2opMGOBD7GVuc4DV0oeDHoA==", + "license": "MIT", + "dependencies": { + "body-parser": "1.20.3", + "cors": "2.8.5", + "express": "4.21.2", + "multer": "1.4.4-lts.1", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0" + } + }, + "node_modules/@nestjs/platform-express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@nestjs/platform-express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@nestjs/platform-express/node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "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.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@nestjs/platform-express/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@nestjs/platform-express/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nestjs/platform-express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-express/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-express/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@nestjs/platform-express/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@nestjs/platform-express/node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-express/node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@nestjs/platform-fastify": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-10.4.15.tgz", + "integrity": "sha512-h1raWeoQEI3Q1wLOLDTbwpObFBMbdq22DlJNyTNY1pyNDyc5K99mnhFnk+eAiM/Di+pjewv5Nh2CzPUZIguA/g==", + "license": "MIT", + "dependencies": { + "@fastify/cors": "9.0.1", + "@fastify/formbody": "7.4.0", + "@fastify/middie": "8.3.3", + "fastify": "4.28.1", + "light-my-request": "6.3.0", + "path-to-regexp": "3.3.0", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@fastify/view": "^7.0.0 || ^8.0.0", + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "@fastify/view": { + "optional": true + } + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/@fastify/ajv-compiler": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", + "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/@fastify/ajv-compiler/node_modules/fast-uri": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-fastify/node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^5.7.0" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/avvio": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz", + "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^3.3.0", + "fastq": "^1.17.1" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/fast-json-stringify": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", + "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.1.0", + "ajv": "^8.10.0", + "ajv-formats": "^3.0.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/fast-json-stringify/node_modules/fast-uri": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-fastify/node_modules/fastify": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.28.1.tgz", + "integrity": "sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^3.5.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^9.0.0", + "process-warning": "^3.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/fastify/node_modules/light-my-request": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz", + "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^0.7.0", + "process-warning": "^3.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/find-my-way": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", + "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^3.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-fastify/node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-fastify/node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "license": "MIT" + }, + "node_modules/@nestjs/platform-fastify/node_modules/ret": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/safe-regex2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", + "license": "MIT", + "dependencies": { + "ret": "~0.4.0" + } + }, + "node_modules/@nestjs/platform-fastify/node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, + "node_modules/@nestjs/platform-socket.io": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.15.tgz", + "integrity": "sha512-KZAxNEADPwoORixh3NJgGYWMVGORVPKeTqjD7hbF8TPDLKWWxru9yasBQwEz2/wXH/WgpkQbbaYwx4nUjCIVpw==", + "license": "MIT", + "dependencies": { + "socket.io": "4.8.1", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/websockets": "^10.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/schedule": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.1.2.tgz", + "integrity": "sha512-hCTQ1lNjIA5EHxeu8VvQu2Ed2DBLS1GSC6uKPYlBiQe6LL9a7zfE9iVSK+zuK8E2odsApteEBmfAQchc8Hx0Gg==", + "license": "MIT", + "dependencies": { + "cron": "3.2.1", + "uuid": "11.0.3" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/@nestjs/schedule/node_modules/uuid": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@nestjs/schematics": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", + "integrity": "sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@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" + }, + "peerDependencies": { + "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/schematics/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nestjs/serve-static": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/serve-static/-/serve-static-5.0.3.tgz", + "integrity": "sha512-0jFjTlSVSLrI+mot8lfm+h2laXtKzCvgsVStv9T1ZBZTDwS26gM5czIhIESmWAod0PfrbCDFiu9C1MglObL8VA==", + "license": "MIT", + "dependencies": { + "path-to-regexp": "8.2.0" + }, + "peerDependencies": { + "@fastify/static": "^8.0.4", + "@nestjs/common": "^11.0.2", + "@nestjs/core": "^11.0.2", + "express": "^5.0.1", + "fastify": "^5.2.1" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "express": { + "optional": true + }, + "fastify": { + "optional": true + } + } + }, + "node_modules/@nestjs/serve-static/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/@nestjs/swagger": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-8.1.1.tgz", + "integrity": "sha512-5Mda7H1DKnhKtlsb0C7PYshcvILv8UFyUotHzxmWh0G65Z21R3LZH/J8wmpnlzL4bmXIfr42YwbEwRxgzpJ5sQ==", + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "^0.15.0", + "@nestjs/mapped-types": "2.0.6", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.3.0", + "swagger-ui-dist": "5.18.2" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/swagger/node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/@nestjs/testing": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.15.tgz", + "integrity": "sha512-eGlWESkACMKti+iZk1hs6FUY/UqObmMaa8HAN9JLnaYkoLf1Jeh+EuHlGnfqo/Rq77oznNLIyaA3PFjrFDlNUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@nestjs/websockets": { + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.15.tgz", + "integrity": "sha512-OmCUJwvtagzXfMVko595O98UI3M9zg+URL+/HV7vd3QPMCZ3uGCKSq15YYJ99LHJn9NyK4e4Szm2KnHtUg2QzA==", + "license": "MIT", + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/platform-socket.io": "^10.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } + } + }, + "node_modules/@nestjs/websockets/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "optional": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.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" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/component-emitter": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.14.tgz", + "integrity": "sha512-lmPil1g82wwWg/qHSxMWkSKyJGQOK+ejXeMAAWyxNtVUD0/Ycj2maL63RAqpxVfdtvTfZkRnqzB0A9ft59y69g==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@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==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "license": "MIT" + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/mjml": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", + "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/mjml-core": "*" + } + }, + "node_modules/@types/mjml-core": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.1.tgz", + "integrity": "sha512-qu8dUksU8yXX18qMTFINkM4uoz7WQYC5F14lcWeSNmWbulaGG0KG19yeZwpx75b9RJXr8WI/FRHH0LyQTU9JbA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/node/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/@types/passport": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz", + "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/superagent": { + "version": "8.1.9", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", + "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.11.9", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz", + "integrity": "sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.22.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "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" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/alce/node_modules/esprima": { + "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" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alce/node_modules/estraverse": { + "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" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.filter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", + "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", + "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "devOptional": true + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "license": "MIT", + "optional": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/avvio": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", + "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, + "node_modules/avvio/node_modules/@fastify/error": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.1.0.tgz", + "integrity": "sha512-KeFcciOr1eo/YvIXHP65S94jfEEqn1RxTRBT1aJaHxY5FK0/GDXYozsQMMWlZoHgi8i0s+YtrLsgj/JkUUjSkQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", + "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.4", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", + "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.4", + "core-js-compat": "^3.33.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", + "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.4" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-walk": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/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/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "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==", + "license": "ISC", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "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", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "license": "MIT", + "optional": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001696", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", + "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-parser": { + "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" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "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, + "license": "MIT" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "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==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "license": "MIT", + "optional": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "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==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/config-chain": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/constantinople": { + "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" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", + "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "dependencies": { + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "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==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cron": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.2.1.tgz", + "integrity": "sha512-w2n5l49GMmmkBFEsH9FIDhjZ1n1QgTMOCMGuQtOXs5veNiosZmso6bQGuqOJSYAXXrG84WQFVneNk+Yt0Ua9iw==", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.5.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "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" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-indent": { + "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" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/display-notification": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "license": "MIT", + "optional": true + }, + "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", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "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" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "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, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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==", + "license": "MIT", + "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", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.90", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz", + "integrity": "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==", + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-japanese": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "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" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-applescript": { + "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" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.19.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-config-standard-jsx": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-11.0.0.tgz", + "integrity": "sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" + } + }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-23.0.0.tgz", + "integrity": "sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/eslint-config-standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/eslint-plugin-n": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", + "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", + "dependencies": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-basic-auth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz", + "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==", + "dependencies": { + "basic-auth": "^2.0.1" + } + }, + "node_modules/express/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/express/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/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==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/express/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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 + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-content-type-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==", + "license": "MIT" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-json-stringify": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.1.tgz", + "integrity": "sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0", + "json-schema-ref-resolver": "^2.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastify": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.3.2.tgz", + "integrity": "sha512-AIPqBgtqBAwkOkrnwesEE+dOyU30dQ4kh7udxeGVR05CRGwubZx+p2H8P0C4cRnQT0+EPK4VGea2DTL2RtWttg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@fastify/ajv-compiler": "^4.0.0", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^9.0.0", + "process-warning": "^5.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" + }, + "node_modules/fastify/node_modules/@fastify/error": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.1.0.tgz", + "integrity": "sha512-KeFcciOr1eo/YvIXHP65S94jfEEqn1RxTRBT1aJaHxY5FK0/GDXYozsQMMWlZoHgi8i0s+YtrLsgj/JkUUjSkQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/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==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/find-my-way": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz", + "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fixpack": { + "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", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "extend-object": "^1.0.0", + "rc": "^1.2.8" + }, + "bin": { + "fixpack": "bin/fixpack" + } + }, + "node_modules/fixpack/node_modules/chalk": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz", + "integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=12.13.0", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", + "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^2.0.0", + "once": "^1.4.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port": { + "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" + }, + "funding": { + "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==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "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" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "optional": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "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" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", + "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "license": "MIT", + "optional": true, + "dependencies": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "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", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-expression": { + "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" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "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" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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 + }, + "node_modules/is-regex": { + "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==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "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" + }, + "engines": { + "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", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jalali-moment": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/jalali-moment/-/jalali-moment-3.3.11.tgz", + "integrity": "sha512-tdSaRs9cjWjOIaWhcsGFZMhZQhfgok5J0TwqFpBIZPudZxxa6yjUPoLCOwuvbAtRpiZn7k/mvazAJh+vEN5suw==", + "dependencies": { + "commander": "^7.0.0", + "inquirer": "^8.0.0", + "moment": "^2.26.0" + }, + "bin": { + "jalalim": "cli.js" + } + }, + "node_modules/jalali-moment/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "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/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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==", + "license": "ISC", + "optional": 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" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-ref-resolver": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz", + "integrity": "sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jstransformer": { + "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" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/juice": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", + "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", + "license": "MIT", + "optional": true, + "dependencies": { + "cheerio": "1.0.0-rc.12", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/juice/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "license": "MIT", + "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==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/kavenegar": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/kavenegar/-/kavenegar-1.1.4.tgz", + "integrity": "sha512-t7oqXS1zZjlFkeofF2J0qSKQeYAmCYogfqROCl/ZQZ3hvCnq9fBBiZIThGnozQ5mMEAJPI5TwWh9RyQ3bOqH3Q==" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leac": { + "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" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libbase64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", + "license": "MIT", + "optional": true + }, + "node_modules/libmime": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.6.tgz", + "integrity": "sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "encoding-japanese": "2.2.0", + "iconv-lite": "0.6.3", + "libbase64": "1.3.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==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.10.55", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.55.tgz", + "integrity": "sha512-MrTg2JFLscgmTY6/oT9vopYETlgUls/FU6OaeeamGwk4LFxjIgOUML/ZSZICgR0LPYXaonVJo40lzMvaaTJlQA==" + }, + "node_modules/libqp": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==", + "license": "MIT", + "optional": true + }, + "node_modules/light-my-request": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.3.0.tgz", + "integrity": "sha512-bWTAPJmeWQH5suJNYwG0f5cs0p6ho9e6f1Ppoxv5qMosY+s9Ir2+ZLvvHcgA7VTDop4zl/NCHhOVVqU+kd++Ow==", + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" + } + }, + "node_modules/light-my-request/node_modules/cookie": { + "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/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/liquidjs": { + "version": "10.20.2", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.20.2.tgz", + "integrity": "sha512-MbAueOtO8aH+GzC/kmhcJTiMrMu+MVel/3+yhFVmP3K89WP0ZuvVPi8ZRKCHAO6SLJvV0Y0Jz6tUEy6Hg8xO/g==", + "license": "MIT", + "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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.compact": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.compact/-/lodash.compact-3.0.1.tgz", + "integrity": "sha512-2ozeiPi+5eBXW1CLtzjk8XQFhQOEMwwfxblqeq6EGyTxZJ1bPATqilY0e6g2SLQpP4KuMeuioBhEnWz5Pr7ICQ==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mailparser": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.2.tgz", + "integrity": "sha512-iI0p2TCcIodR1qGiRoDBBwboSSff50vQAWytM5JRggLfABa4hHYCf3YVujtuzV454xrOP352VsAPIzviqMTo4Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "encoding-japanese": "2.2.0", + "he": "1.2.0", + "html-to-text": "9.0.5", + "iconv-lite": "0.6.3", + "libmime": "5.3.6", + "linkify-it": "5.0.0", + "mailsplit": "5.4.2", + "nodemailer": "6.9.16", + "punycode.js": "2.3.1", + "tlds": "1.255.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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mailparser/node_modules/nodemailer": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", + "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/mailsplit": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.2.tgz", + "integrity": "sha512-4cczG/3Iu3pyl8JgQ76dKkisurZTmxMrA4dj/e8d2jKYcFTZ7MxOzg1gTioTDMPuFXwTrVuN/gxhkrO7wLg7qA==", + "license": "(MIT OR EUPL-1.1+)", + "optional": true, + "dependencies": { + "libbase64": "1.3.0", + "libmime": "5.3.6", + "libqp": "2.1.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "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==", + "license": "MIT", + "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", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "optional": true, + "peer": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/mjml": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.15.3.tgz", + "integrity": "sha512-bW2WpJxm6HS+S3Yu6tq1DUPFoTxU9sPviUSmnL7Ua+oVO3WA5ILFWqvujUlz+oeuM+HCwEyMiP5xvKNPENVjYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "mjml-cli": "4.15.3", + "mjml-core": "4.15.3", + "mjml-migrate": "4.15.3", + "mjml-preset-core": "4.15.3", + "mjml-validator": "4.15.3" + }, + "bin": { + "mjml": "bin/mjml" + } + }, + "node_modules/mjml-accordion": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.15.3.tgz", + "integrity": "sha512-LPNVSj1LyUVYT9G1gWwSw3GSuDzDsQCu0tPB2uDsq4VesYNnU6v3iLCQidMiR6azmIt13OEozG700ygAUuA6Ng==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-body": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.15.3.tgz", + "integrity": "sha512-7pfUOVPtmb0wC+oUOn4xBsAw4eT5DyD6xqaxj/kssu6RrFXOXgJaVnDPAI9AzIvXJ/5as9QrqRGYAddehwWpHQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-button": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.15.3.tgz", + "integrity": "sha512-79qwn9AgdGjJR1vLnrcm2rq2AsAZkKC5JPwffTMG+Nja6zGYpTDZFZ56ekHWr/r1b5WxkukcPj2PdevUug8c+Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-carousel": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.15.3.tgz", + "integrity": "sha512-3ju6I4l7uUhPRrJfN3yK9AMsfHvrYbRkcJ1GRphFHzUj37B2J6qJOQUpzA547Y4aeh69TSb7HFVf1t12ejQxVw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-cli": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.15.3.tgz", + "integrity": "sha512-+V2TDw3tXUVEptFvLSerz125C2ogYl8klIBRY1m5BHd4JvGVf3yhx8N3PngByCzA6PGcv/eydGQN+wy34SHf0Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "chokidar": "^3.0.0", + "glob": "^10.3.10", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "minimatch": "^9.0.3", + "mjml-core": "4.15.3", + "mjml-migrate": "4.15.3", + "mjml-parser-xml": "4.15.3", + "mjml-validator": "4.15.3", + "yargs": "^17.7.2" + }, + "bin": { + "mjml-cli": "bin/mjml" + } + }, + "node_modules/mjml-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mjml-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==", + "license": "ISC", + "optional": 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" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mjml-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==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/mjml-cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mjml-column": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.15.3.tgz", + "integrity": "sha512-hYdEFdJGHPbZJSEysykrevEbB07yhJGSwfDZEYDSbhQQFjV2tXrEgYcFD5EneMaowjb55e3divSJxU4c5q4Qgw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-core": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.15.3.tgz", + "integrity": "sha512-Dmwk+2cgSD9L9GmTbEUNd8QxkTZtW9P7FN/ROZW/fGZD6Hq6/4TB0zEspg2Ow9eYjZXO2ofOJ3PaQEEShKV0kQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "cheerio": "1.0.0-rc.12", + "detect-node": "^2.0.4", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "juice": "^10.0.0", + "lodash": "^4.17.21", + "mjml-migrate": "4.15.3", + "mjml-parser-xml": "4.15.3", + "mjml-validator": "4.15.3" + } + }, + "node_modules/mjml-divider": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.15.3.tgz", + "integrity": "sha512-vh27LQ9FG/01y0b9ntfqm+GT5AjJnDSDY9hilss2ixIUh0FemvfGRfsGVeV5UBVPBKK7Ffhvfqc7Rciob9Spzw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-group": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.15.3.tgz", + "integrity": "sha512-HSu/rKnGZVKFq3ciT46vi1EOy+9mkB0HewO4+P6dP/Y0UerWkN6S3UK11Cxsj0cAp0vFwkPDCdOeEzRdpFEkzA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.15.3.tgz", + "integrity": "sha512-o3mRuuP/MB5fZycjD3KH/uXsnaPl7Oo8GtdbJTKtH1+O/3pz8GzGMkscTKa97l03DAG2EhGrzzLcU2A6eshwFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-attributes": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.15.3.tgz", + "integrity": "sha512-2ISo0r5ZKwkrvJgDou9xVPxxtXMaETe2AsAA02L89LnbB2KC0N5myNsHV0sEysTw9+CfCmgjAb0GAI5QGpxKkQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-breakpoint": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.15.3.tgz", + "integrity": "sha512-Eo56FA5C2v6ucmWQL/JBJ2z641pLOom4k0wP6CMZI2utfyiJ+e2Uuinj1KTrgDcEvW4EtU9HrfAqLK9UosLZlg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-font": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.15.3.tgz", + "integrity": "sha512-CzV2aDPpiNIIgGPHNcBhgyedKY4SX3BJoTwOobSwZVIlEA6TAWB4Z9WwFUmQqZOgo1AkkiTHPZQvGcEhFFXH6g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-html-attributes": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-preview": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.15.3.tgz", + "integrity": "sha512-J2PxCefUVeFwsAExhrKo4lwxDevc5aKj888HBl/wN4EuWOoOg06iOGCxz4Omd8dqyFsrqvbBuPqRzQ+VycGmaA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-style": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-title": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-hero": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-image": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.15.3.tgz", + "integrity": "sha512-g1OhSdofIytE9qaOGdTPmRIp7JsCtgO0zbsn1Fk6wQh2gEL55Z40j/VoghslWAWTgT2OHFdBKnMvWtN6U5+d2Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-migrate": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.15.3.tgz", + "integrity": "sha512-sr/+35RdxZroNQVegjpfRHJ5hda9XCgaS4mK2FGO+Mb1IUevKfeEPII3F/cHDpNwFeYH3kAgyqQ22ClhGLWNBA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "mjml-core": "4.15.3", + "mjml-parser-xml": "4.15.3", + "yargs": "^17.7.2" + }, + "bin": { + "migrate": "lib/cli.js" + } + }, + "node_modules/mjml-navbar": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.15.3.tgz", + "integrity": "sha512-VsKH/Jdlf8Yu3y7GpzQV5n7JMdpqvZvTSpF6UQXL0PWOm7k6+LX+sCZimOfpHJ+wCaaybpxokjWZ71mxOoCWoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-parser-xml": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "detect-node": "2.1.0", + "htmlparser2": "^9.1.0", + "lodash": "^4.17.15" + } + }, + "node_modules/mjml-parser-xml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/mjml-preset-core": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.15.3.tgz", + "integrity": "sha512-1zZS8P4O0KweWUqNS655+oNnVMPQ1Rq1GaZq5S9JfwT1Vh/m516lSmiTW9oko6gGHytt5s6Yj6oOeu5Zm8FoLw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "mjml-accordion": "4.15.3", + "mjml-body": "4.15.3", + "mjml-button": "4.15.3", + "mjml-carousel": "4.15.3", + "mjml-column": "4.15.3", + "mjml-divider": "4.15.3", + "mjml-group": "4.15.3", + "mjml-head": "4.15.3", + "mjml-head-attributes": "4.15.3", + "mjml-head-breakpoint": "4.15.3", + "mjml-head-font": "4.15.3", + "mjml-head-html-attributes": "4.15.3", + "mjml-head-preview": "4.15.3", + "mjml-head-style": "4.15.3", + "mjml-head-title": "4.15.3", + "mjml-hero": "4.15.3", + "mjml-image": "4.15.3", + "mjml-navbar": "4.15.3", + "mjml-raw": "4.15.3", + "mjml-section": "4.15.3", + "mjml-social": "4.15.3", + "mjml-spacer": "4.15.3", + "mjml-table": "4.15.3", + "mjml-text": "4.15.3", + "mjml-wrapper": "4.15.3" + } + }, + "node_modules/mjml-raw": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.15.3.tgz", + "integrity": "sha512-IGyHheOYyRchBLiAEgw3UM11kFNmBSMupu2BDdejC6ZiDhEAdG+tyERlsCwDPYtXanvFpGWULIu3XlsUPc+RZw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-section": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.15.3.tgz", + "integrity": "sha512-JfVPRXH++Hd933gmQfG8JXXCBCR6fIzC3DwiYycvanL/aW1cEQ2EnebUfQkt5QzlYjOkJEH+JpccAsq3ln6FZQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-social": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.15.3.tgz", + "integrity": "sha512-7sD5FXrESOxpT9Z4Oh36bS6u/geuUrMP1aCg2sjyAwbPcF1aWa2k9OcatQfpRf6pJEhUZ18y6/WBBXmMVmSzXg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-spacer": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.15.3.tgz", + "integrity": "sha512-3B7Qj+17EgDdAtZ3NAdMyOwLTX1jfmJuY7gjyhS2HtcZAmppW+cxqHUBwCKfvSRgTQiccmEvtNxaQK+tfyrZqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-table": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.15.3.tgz", + "integrity": "sha512-FLx7DcRKTdKdcOCbMyBaeudeHaHpwPveRrBm6WyQe3LXx6FfdmOh59i71/16LFQMgBOD3N4/UJkzxLzlTJzMqQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-text": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.15.3.tgz", + "integrity": "sha512-+C0hxCmw9kg0XzT6vhE5mFkK6y225nC8UEQcN94K0fBCjPKkM+HqZMwGX205fzdGRi+Bxa55b/VhrIVwdv+8vw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-validator": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.15.3.tgz", + "integrity": "sha512-Xb72KdqRwjv/qM2rJpV22syyP2N3cRQ9VVDrN6u2FSzLq02buFNxmSPJ7CKhat3PrUNdVHU75KZwOf/tz4UEhA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9" + } + }, + "node_modules/mjml-wrapper": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.15.3.tgz", + "integrity": "sha512-ditsCijeHJrmBmObtJmQ18ddLxv5oPyMTdPU8Di8APOnD2zPk7Z4UAuJSl7HXB45oFiivr3MJf4koFzMUSZ6Gg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3", + "mjml-section": "4.15.3" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mnemonist": { + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", + "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.1" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/mongodb": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "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==", + "license": "MIT", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongoose": { + "version": "8.9.6", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.6.tgz", + "integrity": "sha512-ipLvXwNPVuuuq5H3lnSD0lpaRH3DlCoC6emnMVJvweTwxU29uxDJWxMsNpERDQt8JMvYF1HGVuTK+Id2BlQLCA==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.1", + "kareem": "2.6.3", + "mongodb": "~6.12.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "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==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "devOptional": true + }, + "node_modules/nestjs-command": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/nestjs-command/-/nestjs-command-3.1.4.tgz", + "integrity": "sha512-0RJGR/Z1ePrf/tzvVJjM95U0sJQeMjFCFad1FqWy4ypQ+VxIG9fGkEf6i8zckQOYjHXBZoWG5YwX+/v4Nof/nA==", + "dependencies": { + "lodash.compact": "^3.0.1", + "lodash.flattendeep": "^4.4.0" + }, + "bin": { + "nestjs-command": "bin/cli" + }, + "peerDependencies": { + "@nestjs/common": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "yargs": "^16.0.0 || ^17.0.0" + } + }, + "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 + }, + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/nodemailer": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "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" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nth-check": { + "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==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", + "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", + "dependencies": { + "array.prototype.filter": "^1.0.3", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-event": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "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" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-wait-for": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "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==", + "devOptional": true, + "license": "BlueOak-1.0.0" + }, + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?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" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", + "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/peberminta": { + "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" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pino": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", + "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, + "node_modules/pino/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dependencies": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/preview-email": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz", + "integrity": "sha512-DBS3Nir18YtKc8loYCCOGitmiaQ0vTdahPoiXxwNweJDpmVZo+w3tppufOhoK0m8skpRxT56llYLs3VrORnmNQ==", + "optional": true, + "dependencies": { + "ci-info": "^3.8.0", + "display-notification": "2.0.0", + "fixpack": "^4.0.0", + "get-port": "5.1.1", + "mailparser": "^3.6.4", + "nodemailer": "^6.9.2", + "open": "7", + "p-event": "4.2.0", + "p-wait-for": "3.2.0", + "pug": "^3.0.2", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/preview-email/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true + }, + "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" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "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==", + "license": "ISC", + "optional": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pug": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", + "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", + "license": "MIT", + "optional": true, + "dependencies": { + "pug-code-gen": "^3.0.3", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "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", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "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", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "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" + } + }, + "node_modules/pug-load": { + "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" + } + }, + "node_modules/pug-parser": { + "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" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "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" + } + }, + "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 + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "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", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "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" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/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" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/run-applescript": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "license": "MIT", + "optional": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "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", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/get-stream": { + "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" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "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" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "optional": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/semver": { + "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" + } + }, + "node_modules/run-applescript/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "optional": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/shebang-regex": { + "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" + } + }, + "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" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex2": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", + "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ret": "~0.5.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/secure-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", + "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "optional": true, + "peer": true + }, + "node_modules/selderee": { + "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" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/send/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/short-unique-id": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz", + "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==", + "license": "Apache-2.0", + "bin": { + "short-unique-id": "bin/short-unique-id", + "suid": "bin/short-unique-id" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, + "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==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slick": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", + "license": "MIT (http://mootools.net/license.txt)", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard": { + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.2.tgz", + "integrity": "sha512-WLm12WoXveKkvnPnPnaFUUHuOB2cUdAsJ4AiGHL2G0UNMrcRAWY2WriQaV8IQ3oRmYr0AWUbLNr94ekYFAHOrA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "eslint": "^8.41.0", + "eslint-config-standard": "17.1.0", + "eslint-config-standard-jsx": "^11.0.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.7.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.36.1", + "standard-engine": "^15.1.0", + "version-guard": "^1.1.1" + }, + "bin": { + "standard": "bin/cmd.cjs" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/standard-engine": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-15.1.0.tgz", + "integrity": "sha512-VHysfoyxFu/ukT+9v49d4BRXIokFRZuH3z1VRxzFArZdjSCFpro6rEIU3ji7e4AoAtuSfKBkiOmsrDqKW5ZSRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "get-stdin": "^8.0.0", + "minimist": "^1.2.6", + "pkg-conf": "^3.1.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/standard/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/standard/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/standard/node_modules/eslint": { + "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.", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/standard/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/standard/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/standard/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/standard/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/standard/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/standard/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/standardjs": { + "version": "1.0.0-alpha", + "resolved": "https://registry.npmjs.org/standardjs/-/standardjs-1.0.0-alpha.tgz", + "integrity": "sha512-ZInCIyftuVwXrBb1qSOwSFlI7YPe13pF3GdEZ4qD8yH/V5YBA4TlPDM34x32F4/VzRHpkoZIUQqrMTq0DNLbZA==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "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" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", + "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^3.5.1", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", + "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^9.0.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz", + "integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/terser": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/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, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "license": "MIT", + "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", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/tlds": { + "version": "1.255.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.255.0.tgz", + "integrity": "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==", + "license": "MIT", + "optional": true, + "bin": { + "tlds": "bin.js" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "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, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "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 + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-loader": { + "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, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-standard": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/ts-standard/-/ts-standard-12.0.2.tgz", + "integrity": "sha512-XX2wrB9fKKTfBj4yD3ABm9iShzZcS2iWcPK8XzlBvuL20+wMiLgiz/k5tXgZwTaYq5wRhbks1Y9PelhujF/9ag==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^8.0.1", + "eslint-config-standard-jsx": "^11.0.0", + "eslint-config-standard-with-typescript": "^23.0.0", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-react": "^7.28.0", + "minimist": "^1.2.6", + "pkg-conf": "^4.0.0", + "standard-engine": "^15.0.0" + }, + "bin": { + "ts-standard": "cli.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/ts-standard/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/ts-standard/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ts-standard/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/ts-standard/node_modules/eslint": { + "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, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/ts-standard/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, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ts-standard/node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-standard/node_modules/eslint/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/ts-standard/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/ts-standard/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/ts-standard/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ts-standard/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/ts-standard/node_modules/pkg-conf": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", + "integrity": "sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==", + "dev": true, + "dependencies": { + "find-up": "^6.0.0", + "load-json-file": "^7.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-standard/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.28.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.28.2.tgz", + "integrity": "sha512-W71OLwDqzIO0d3k07qg1xc7d4cX8SsSwuCO4bQ4V7ITwduXXie/lcImofabP5VV+NvuvSe8ovKvHVJcizVc1JA==" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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==", + "license": "MIT", + "optional": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/version-guard": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/version-guard/-/version-guard-1.1.1.tgz", + "integrity": "sha512-MGQLX89UxmYHgDvcXyjBI0cbmoW+t/dANDppNPrno64rYr8nH4SHSuElQuSYdXGEs0mUzdQe1BY+FhVPNsAmJQ==", + "engines": { + "node": ">=0.10.48" + } + }, + "node_modules/void-elements": { + "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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-resource-inliner": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "optional": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "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/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, + "license": "MIT", + "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/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/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, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "optional": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e36dd5b --- /dev/null +++ b/package.json @@ -0,0 +1,109 @@ +{ + "name": "yara724", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@arashioz/errjson-talieh": "^2.2.5", + "@fraybabak/kavenegar_nest": "^1.0.5", + "@nestjs-modules/mailer": "^2.0.2", + "@nestjs/axios": "^3.1.3", + "@nestjs/common": "^10.4.15", + "@nestjs/core": "^10.4.15", + "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "*", + "@nestjs/mongoose": "^10.1.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-express": "^10.4.15", + "@nestjs/platform-fastify": "^10.4.15", + "@nestjs/platform-socket.io": "^10.4.15", + "@nestjs/schedule": "^4.1.2", + "@nestjs/serve-static": "^5.0.3", + "@nestjs/swagger": "^8.1.0", + "@nestjs/websockets": "^10.4.15", + "@types/uuid": "^10.0.0", + "axios": "^1.9.0", + "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "crypto": "^1.0.1", + "dotenv": "^16.4.7", + "express-basic-auth": "^1.2.1", + "fastest-levenshtein": "^1.0.16", + "form-data": "^4.0.2", + "jalali-moment": "^3.3.11", + "kavenegar": "^1.1.4", + "mongoose": "^8.9.2", + "nestjs-command": "^3.1.4", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "short-unique-id": "^5.2.0", + "standard": "^17.1.2", + "standardjs": "^1.0.0-alpha", + "uuid": "^11.0.3", + "yargs": "^17.7.2" + }, + "devDependencies": { + "@nestjs/cli": "^10.4.9", + "@nestjs/schematics": "^10.2.3", + "@nestjs/testing": "^10.4.15", + "@types/express": "^5.0.0", + "@types/jest": "^29.5.14", + "@types/multer": "^1.4.12", + "@types/node": "^22.10.2", + "@types/passport-jwt": "^4.0.1", + "@types/supertest": "^6.0.2", + "@types/yargs": "^17.0.33", + "@typescript-eslint/eslint-plugin": "^8.18.1", + "@typescript-eslint/parser": "^8.18.1", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "jest": "^29.7.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "ts-standard": "^12.0.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.2" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/src/Types&Enums/blame-request-management/accident-conditions.enum.ts b/src/Types&Enums/blame-request-management/accident-conditions.enum.ts new file mode 100644 index 0000000..0a84b5f --- /dev/null +++ b/src/Types&Enums/blame-request-management/accident-conditions.enum.ts @@ -0,0 +1,23 @@ +// Weather conditions for accident +export enum WeatherCondition { + CLEAR = "صاف", + RAINY = "بارونی", + SNOWY = "برفی", + FOGGY = "مه آلود", +} + +// Road conditions for accident +export enum RoadCondition { + MUDDY = "گلی", + ICY = "یخ زده", + WET = "مرطوب", + DRY = "خشک", +} + +// Light conditions for accident +export enum LightCondition { + DAYLIGHT = "روز", + NIGHT = "شب", + LOW_LIGHT = "کم نور", +} + diff --git a/src/Types&Enums/blame-request-management/status.enum.ts b/src/Types&Enums/blame-request-management/status.enum.ts new file mode 100644 index 0000000..f239b5c --- /dev/null +++ b/src/Types&Enums/blame-request-management/status.enum.ts @@ -0,0 +1,14 @@ +export enum ReqBlameStatus { + PendingForSecondParty = "PendingForSecondParty", + PendingForFirstParty = "PendingForFirstParty", + UnChecked = "UnChecked", + CheckedRequest = "CheckedRequest", + ReviewRequest = "ReviewRequest", + CheckAgain = "CheckAgain", + UserPending = "UserPending", + CloseRequest = "CloseRequest", + WaitForUserAccept = "WaitForUserAccept", + WaitingForSignatures = "WaitingForSignatures", + PartiesDisagree = "PartiesDisagree", + InPersonVisit = "InPersonVisit", +} diff --git a/src/Types&Enums/blame-request-management/steps.enum.ts b/src/Types&Enums/blame-request-management/steps.enum.ts new file mode 100644 index 0000000..8695099 --- /dev/null +++ b/src/Types&Enums/blame-request-management/steps.enum.ts @@ -0,0 +1,26 @@ +export enum StepsEnum { + createRequest = "createRequest", + F_InitialForm = "firstParty-initialForm", + F_addPlate = "firstParty-addPlate", + F_videoUpload = "firstParty-videoUpload", + F_addLocation = "firstParty-addLocation", + F_addVoice = "firstParty-addVoice", + F_addDescription = "firstParty-addDescription", + F_addSecondPartyPhoneNumber = "firstParty-addSecondPartyPhoneNumber", + F_addSign = "firstParty-addSign", + F_completed = "firstParty-completed", + S_InitialForm = "secondParty-initialForm", + S_addPlate = "secondParty-addPlate", + S_addLocation = "secondParty-addLocation", + S_addVoice = "secondParty-addVoice", + S_addDescription = "secondParty-addDescription", + S_addSecondPartyPhoneNumber = "S_addSecondPartyPhoneNumber", + S_addSign = "secondParty-addSign", + S_completed = "secondParty-completed", + AddSignatures = "AddSignatures", + CarBodyVideo = "CarBodyVideo", + CarBodyForm = "CarBodyForm", + CarBodySecondForm = "CarBodySecondForm", + CarBodyDamaged = "CarBodyDamaged", + CarBodyGuilty = "CarBodyGuilty", +} diff --git a/src/Types&Enums/claim-request-management/car-part.enum.ts b/src/Types&Enums/claim-request-management/car-part.enum.ts new file mode 100644 index 0000000..61cbd50 --- /dev/null +++ b/src/Types&Enums/claim-request-management/car-part.enum.ts @@ -0,0 +1,21 @@ +export enum CarPartEnum { + LeftBackFender = "leftBackFender", + RightBackFender = "rightBackFender", + BackWheel = "backWheel", + LeftBackDoor = "leftBackDoor", + RightBackDoor = "rightBackDoor", + LeftFrontDoor = "leftFrontDoor", + RightFrontDoor = "rightFrontDoor", + LeftMirror = "leftMirror", + RightMirror = "rightMirror", + FrontWheel = "frontWheel", + LeftFrontFender = "leftFrontFender", + RightFrontFender = "RightFrontFender", + FrontBumper = "frontBumper", + FrontCarWindow = "frontCarWindow", + CarHood = "carHood", + BackBumper = "backBumper", + CarTrunk = "carTrunk", + BackCarWindow = "backCarWindow", + Roof = "roof", +} diff --git a/src/Types&Enums/claim-request-management/daghi-option.enum.ts b/src/Types&Enums/claim-request-management/daghi-option.enum.ts new file mode 100644 index 0000000..e1dee2d --- /dev/null +++ b/src/Types&Enums/claim-request-management/daghi-option.enum.ts @@ -0,0 +1,7 @@ +export enum DaghiOption { + RECYCLED_PARTS_VALUE = "ارزش لوازم بازیافتی", + DELIVER_DAMAGED_PART = "تحویل داغی", + NO_VALUE = "فاقد ارزش", + WITH_DAMAGED_PART_CALCULATION = "با احتساب داغی", +} + diff --git a/src/Types&Enums/claim-request-management/dropPrice.interface.ts b/src/Types&Enums/claim-request-management/dropPrice.interface.ts new file mode 100644 index 0000000..41c691c --- /dev/null +++ b/src/Types&Enums/claim-request-management/dropPrice.interface.ts @@ -0,0 +1,7 @@ +export class PriceDropIF { + total: number; + carPrice: number; + carModel: number; + carValue: number[]; + sumOfSeverity: number; +} diff --git a/src/Types&Enums/claim-request-management/factor-status.enum.ts b/src/Types&Enums/claim-request-management/factor-status.enum.ts new file mode 100644 index 0000000..79ab091 --- /dev/null +++ b/src/Types&Enums/claim-request-management/factor-status.enum.ts @@ -0,0 +1,5 @@ +export enum FactorStatus { + PENDING = "PENDING", + APPROVED = "APPROVED", + REJECTED = "REJECTED", +} diff --git a/src/Types&Enums/claim-request-management/in-person-dcouments-enum.ts b/src/Types&Enums/claim-request-management/in-person-dcouments-enum.ts new file mode 100644 index 0000000..bc8b48e --- /dev/null +++ b/src/Types&Enums/claim-request-management/in-person-dcouments-enum.ts @@ -0,0 +1,6 @@ +export enum InPersonDocumentsEnum { + NationalCertificate = "nationalCertificate", + CarCertificate = "carCertificate", + DrivingLicense = "drivingLicense", + CarGreenCard = "carGreenCard", +} diff --git a/src/Types&Enums/claim-request-management/required-document-type.enum.ts b/src/Types&Enums/claim-request-management/required-document-type.enum.ts new file mode 100644 index 0000000..b32fe5e --- /dev/null +++ b/src/Types&Enums/claim-request-management/required-document-type.enum.ts @@ -0,0 +1,18 @@ +export enum ClaimRequiredDocumentType { + // Damaged party documents + DAMAGED_DRIVING_LICENSE_BACK = "damaged_driving_license_back", + DAMAGED_DRIVING_LICENSE_FRONT = "damaged_driving_license_front", + DAMAGED_CHASSIS_NUMBER = "damaged_chassis_number", + DAMAGED_ENGINE_PHOTO = "damaged_engine_photo", + DAMAGED_CAR_CARD_FRONT = "damaged_car_card_front", + DAMAGED_CAR_CARD_BACK = "damaged_car_card_back", + DAMAGED_METAL_PLATE = "damaged_metal_plate", + + // Guilty party documents + GUILTY_DRIVING_LICENSE_FRONT = "guilty_driving_license_front", + GUILTY_DRIVING_LICENSE_BACK = "guilty_driving_license_back", + GUILTY_CAR_CARD_FRONT = "guilty_car_card_front", + GUILTY_CAR_CARD_BACK = "guilty_car_card_back", + GUILTY_METAL_PLATE = "guilty_metal_plate", +} + diff --git a/src/Types&Enums/claim-request-management/status.enum.ts b/src/Types&Enums/claim-request-management/status.enum.ts new file mode 100644 index 0000000..e12e0f4 --- /dev/null +++ b/src/Types&Enums/claim-request-management/status.enum.ts @@ -0,0 +1,15 @@ +export enum ReqClaimStatus { + WaitingForUserCompleted = "WaitingForUserCompleted", + UnChecked = "UnChecked", + CheckedRequest = "CheckedRequest", + ReviewRequest = "ReviewRequest", + CheckAgain = "CheckAgain", + UserPending = "UserPending", + CloseRequest = "CloseRequest", + WaitingForUserToResend = "WaitingForUserToResend", + InPersonVisit = "InPersonVisit", + PendingFactorUpload = "PendingFactorUpload", + PendingFactorValidation = "PendingFactorValidation", + FactorRejected = "FactorRejected", + UploadingRequiredDocuments = "UploadingRequiredDocuments", +} diff --git a/src/Types&Enums/claim-request-management/steps.enum.ts b/src/Types&Enums/claim-request-management/steps.enum.ts new file mode 100644 index 0000000..09b712d --- /dev/null +++ b/src/Types&Enums/claim-request-management/steps.enum.ts @@ -0,0 +1,10 @@ +export enum ClaimStepsEnum { + CreateClaimFile = "createClaimFile", + SelectDamagePart = "selectDamagePart", + SelectOtherParts = "SelectOtherParts", + UploadRequiredDocuments = "uploadRequiredDocuments", + ImageRequired = "ImageRequired", + waitForDamageExpertComment = "waitForDamageExpertComment", + WaitingForUserToReact = "WaitingForUserToReact", + WaitingForFactorUpload = "WaitingForFactorUpload", +} diff --git a/src/Types&Enums/claim-request-management/type-of-damage.enum.ts b/src/Types&Enums/claim-request-management/type-of-damage.enum.ts new file mode 100644 index 0000000..3b10a39 --- /dev/null +++ b/src/Types&Enums/claim-request-management/type-of-damage.enum.ts @@ -0,0 +1,4 @@ +export enum TypeOfDamage { + Repair = "repair", + Change = "change", +} diff --git a/src/Types&Enums/claim-request-management/userReply.enum.ts b/src/Types&Enums/claim-request-management/userReply.enum.ts new file mode 100644 index 0000000..061c2a7 --- /dev/null +++ b/src/Types&Enums/claim-request-management/userReply.enum.ts @@ -0,0 +1,5 @@ +export enum UserReplyEnum { + HOLD = "HOLD", + ACCEPTED = "ACCEPTED", + REJECTED = "REJECTED", +} diff --git a/src/Types&Enums/damage-expert.enum.ts b/src/Types&Enums/damage-expert.enum.ts new file mode 100644 index 0000000..d71319a --- /dev/null +++ b/src/Types&Enums/damage-expert.enum.ts @@ -0,0 +1,32 @@ +export enum ExpertizedAtEnum { + BADANE = "badane", + SAALES = "saales", + FANI = "fani", + SPECIALIZED = "specialized", +} + +export enum PreviousWorkEnum { + INSURANCE_COMPANY = "insuranceCompany", + BROKER = "broker", + GENUINE_EXPERT = "genuineExpert", +} + +export enum SkillEnum { + SMOOTHING = "smothing", + COLORING = "coloring", + PARTS_AUTH = "partsAuth", + MEDIA_EVAL = "mediaEval", + SCENE_EXPERT = "sceneExpert", + DYNAMIC_ANALYSIS = "dynamicAnalysis", +} + + + + + + + + + + + diff --git a/src/Types&Enums/degrees.enum.ts b/src/Types&Enums/degrees.enum.ts new file mode 100644 index 0000000..530469b --- /dev/null +++ b/src/Types&Enums/degrees.enum.ts @@ -0,0 +1,6 @@ +export enum Degrees { + DIPLOMA = "diploma", + EXPERT = "expert", + SENIOR_EXPERT = "senior_expert", + PHD = "phd", +} diff --git a/src/Types&Enums/plate.interface.ts b/src/Types&Enums/plate.interface.ts new file mode 100644 index 0000000..b89de43 --- /dev/null +++ b/src/Types&Enums/plate.interface.ts @@ -0,0 +1,8 @@ +export interface Plates { + leftDigits: number; + centerAlphabet: string; + centerDigits: number; + ir: number; + nationalCode: string; + carDetail?: any; +} diff --git a/src/Types&Enums/role.enum.ts b/src/Types&Enums/role.enum.ts new file mode 100644 index 0000000..beb0889 --- /dev/null +++ b/src/Types&Enums/role.enum.ts @@ -0,0 +1,7 @@ +export enum RoleEnum { + EXPERT = "expert", + DAMAGE_EXPERT = "damage_expert", + COMPANY = "company", + ADMIN = "admin", + USER = "user", +} diff --git a/src/Types&Enums/upload.enum.ts b/src/Types&Enums/upload.enum.ts new file mode 100644 index 0000000..8b9fbd4 --- /dev/null +++ b/src/Types&Enums/upload.enum.ts @@ -0,0 +1,4 @@ +export enum UploaderModeEnum { + Sign = "uploadService", + Certificate = "certService", +} diff --git a/src/Types&Enums/userType.enum.ts b/src/Types&Enums/userType.enum.ts new file mode 100644 index 0000000..01fe1aa --- /dev/null +++ b/src/Types&Enums/userType.enum.ts @@ -0,0 +1,5 @@ +export enum UserType { + GENUINE = "genuine", + LEGAL = "legal", + INSURER = "insurer", +} diff --git a/src/ai/ai.module.ts b/src/ai/ai.module.ts new file mode 100644 index 0000000..dc61f17 --- /dev/null +++ b/src/ai/ai.module.ts @@ -0,0 +1,10 @@ +import { HttpModule } from "@nestjs/axios"; +import { Module } from "@nestjs/common"; +import { AiService } from "./ai.service"; + +@Module({ + imports: [HttpModule], + providers: [AiService], + exports: [AiService], +}) +export class AiModule {} diff --git a/src/ai/ai.service.ts b/src/ai/ai.service.ts new file mode 100644 index 0000000..1eff579 --- /dev/null +++ b/src/ai/ai.service.ts @@ -0,0 +1,273 @@ +import { createReadStream, existsSync } from "node:fs"; +import { join } from "node:path"; +import { + HttpException, + HttpStatus, + Injectable, + Logger, + OnModuleInit, +} from "@nestjs/common"; +import axios, { AxiosRequestConfig } from "axios"; +import * as FormData from "form-data"; + +@Injectable() +export class AiService implements OnModuleInit { + private readonly logger = new Logger(AiService.name); + private apiKey: string; + private accessToken: string = null; + + // These configurations are for authentication and getting the API key. + private readonly loginOptions: AxiosRequestConfig = { + method: "POST", + headers: { "Content-Type": "application/json" }, + url: `${process.env.AI_URL_V2}/auth/login`, + data: { + username: process.env.AI_USERNAME, + password: process.env.AI_PASSWORD, + }, + timeout: 30000, // 30 second timeout + }; + + private get profileOptions(): AxiosRequestConfig { + return { + method: "GET", + url: `${process.env.AI_URL_V2}/auth/profile`, + headers: { + Authorization: `Bearer ${this.accessToken}`, + }, + }; + } + + // This getter dynamically creates the base options for the image processing request. + private get imageProcessOptions(): AxiosRequestConfig { + return { + method: "POST", + url: `${process.env.AI_URL_V2}/services/car-damage/detector?version=ai-v7`, + headers: { + Authorization: `Bearer ${this.accessToken}`, + "gateway-api-key": `${this.apiKey}`, + }, + }; + } + + constructor() {} + + async onModuleInit() { + try { + const res = await this.login(); + if (res?.accessToken) { + this.logger.verbose("AI Service Authenticated Successfully."); + this.accessToken = res.accessToken; + await this.getApiKey(); + this.logger.log("AI Service initialized and ready."); + } else { + this.logger.warn( + "AI Service Unavailable: Login did not return an access token. Will retry on first request.", + ); + } + } catch (error) { + // Don't prevent app startup if AI service is temporarily unavailable + // The service will attempt to re-authenticate when aiRequestImage is called + this.logger.warn( + "AI Service Unavailable: Failed during initial login. Will retry on first request.", + ); + this.logger.warn(`Error: ${error.message}`); + // Reset tokens so re-authentication will be attempted + this.accessToken = null; + this.apiKey = null; + } + } + + private async login() { + try { + const loginResponse = await axios.request(this.loginOptions); + return loginResponse.data; + } catch (err) { + const errorMessage = err.response?.data?.message || err.message || "Unknown error"; + const statusCode = err.response?.status || 500; + this.logger.error(`AI login failed: ${errorMessage} (Status: ${statusCode})`); + if (err.response?.data) { + this.logger.error(`AI login error details: ${JSON.stringify(err.response.data, null, 2)}`); + } + throw new HttpException( + `Could not authenticate with AI service: ${errorMessage}`, + statusCode >= 400 && statusCode < 500 ? statusCode : HttpStatus.UNAUTHORIZED, + ); + } + } + + private async getApiKey() { + try { + const profileResponse = await axios.request(this.profileOptions); + this.apiKey = profileResponse.data.apiKey.key; + this.logger.log("Successfully retrieved AI gateway API key."); + return this.apiKey; + } catch (err) { + this.logger.error("Failed to retrieve AI API key:", err.message); + throw new HttpException( + "Could not get API key from AI service", + HttpStatus.FAILED_DEPENDENCY, + ); + } + } + + public async aiRequestImage(file: { path: string; fileName?: string }): Promise { + // Ensure authentication is set up + if (!this.accessToken || !this.apiKey) { + this.logger.warn("AI service not authenticated, attempting to re-authenticate..."); + try { + const res = await this.login(); + if (res?.accessToken) { + this.accessToken = res.accessToken; + await this.getApiKey(); + } else { + throw new HttpException( + "AI Service authentication failed", + HttpStatus.UNAUTHORIZED, + ); + } + } catch (error) { + this.logger.error("Failed to re-authenticate AI service:", error.message); + throw new HttpException( + "AI Service authentication failed", + HttpStatus.UNAUTHORIZED, + ); + } + } + + // Resolve relative paths to absolute paths + const filePath = file.path.startsWith("/") + ? file.path + : join(process.cwd(), file.path.replace(/^\.\//, "")); + + this.logger.log(`Processing AI image request for: ${filePath}`); + + // Check if file exists + if (!existsSync(filePath)) { + this.logger.error(`File not found at path: ${filePath}`); + throw new HttpException( + `File not found: ${file.path}`, + HttpStatus.NOT_FOUND, + ); + } + + const form = new FormData(); + const fileStream = createReadStream(filePath); + + // Append file with filename if available + if (file.fileName) { + form.append("images", fileStream, file.fileName); + } else { + // Extract filename from path if not provided + const pathParts = filePath.split("/"); + const extractedFileName = pathParts[pathParts.length - 1]; + form.append("images", fileStream, extractedFileName); + } + + try { + const requestHeaders = { + ...this.imageProcessOptions.headers, + ...form.getHeaders(), + }; + + this.logger.log(`[STEP 1/4] Sending request to AI service: ${this.imageProcessOptions.url}`); + this.logger.log(`[STEP 1/4] File: ${filePath}, Filename: ${file.fileName || 'extracted from path'}`); + this.logger.log(`[STEP 1/4] FormData Content-Type: ${form.getHeaders()['content-type']}`); + this.logger.log(`[STEP 1/4] Authorization header present: ${!!requestHeaders.Authorization}`); + this.logger.log(`[STEP 1/4] Gateway API key present: ${!!this.apiKey}`); + this.logger.log(`[STEP 1/4] Request method: POST`); + this.logger.log(`[STEP 1/4] FormData field name: "images"`); + + // Get file stats for debugging + const fs = require('fs'); + const stats = fs.statSync(filePath); + this.logger.log(`[STEP 1/4] File size: ${stats.size} bytes`); + this.logger.log(`[STEP 1/4] File exists: ${existsSync(filePath)}`); + + const response = await axios.request({ + ...this.imageProcessOptions, + headers: requestHeaders, + data: form, + maxContentLength: Infinity, + maxBodyLength: Infinity, + }); + + this.logger.log(`[STEP 2/4] Successfully received response from AI service (Status: ${response.status})`); + + // Validate response structure + if (!response.data) { + this.logger.error(`[ERROR] AI response is empty or missing data`); + throw new HttpException( + "AI Service returned empty response", + HttpStatus.BAD_GATEWAY, + ); + } + + // Check for error in response first (AI service returns 201 with error in body) + if (response.data.error) { + this.logger.error(`[ERROR] AI service returned an error in response body`); + this.logger.error(`[ERROR] Error message: ${response.data.error}`); + this.logger.error(`[ERROR] Full response: ${JSON.stringify(response.data, null, 2)}`); + throw new HttpException( + `AI Service error: ${response.data.error}`, + HttpStatus.BAD_GATEWAY, + ); + } + + // Check for processed image (downloadLink) + if (!response.data.downloadLink) { + this.logger.error(`[ERROR] AI response missing processed image (downloadLink)`); + this.logger.error(`[ERROR] Response structure: ${JSON.stringify(Object.keys(response.data))}`); + this.logger.error(`[ERROR] Full response: ${JSON.stringify(response.data, null, 2)}`); + throw new HttpException( + "AI Service did not return processed image (downloadLink missing)", + HttpStatus.BAD_GATEWAY, + ); + } + + // Check for reports + if (!response.data.reports) { + this.logger.warn(`[WARNING] AI response missing reports object, but downloadLink exists`); + this.logger.warn(`[WARNING] Response keys: ${JSON.stringify(Object.keys(response.data))}`); + } + + this.logger.log(`[STEP 3/4] Validated AI response - downloadLink: ${response.data.downloadLink ? 'present' : 'missing'}, reports: ${response.data.reports ? 'present' : 'missing'}`); + + return response.data; + } catch (er) { + // Determine error source + let errorSource = "UNKNOWN"; + let errorMessage = er.message; + let errorDetails = "No error details available"; + + if (er.response) { + errorSource = "AI_SERVICE_RESPONSE"; + errorMessage = er.response?.data?.message || er.message || `HTTP ${er.response.status}`; + errorDetails = er.response?.data + ? JSON.stringify(er.response.data, null, 2) + : `Status: ${er.response.status}, StatusText: ${er.response.statusText}`; + } else if (er.request) { + errorSource = "NETWORK_ERROR"; + errorMessage = "Network error - AI service did not respond"; + errorDetails = "Request was made but no response received"; + } else { + errorSource = "REQUEST_SETUP_ERROR"; + errorMessage = er.message || "Error setting up request"; + } + + this.logger.error(`[ERROR] AI request failed - Source: ${errorSource}`); + this.logger.error(`[ERROR] File path: ${filePath}`); + this.logger.error(`[ERROR] Error message: ${errorMessage}`); + this.logger.error(`[ERROR] Error details: ${errorDetails}`); + if (er.stack) { + this.logger.error(`[ERROR] Stack trace: ${er.stack}`); + } + + // Re-throw with detailed error information + throw new HttpException( + `[${errorSource}] ${errorMessage}`, + er.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/app.module.ts b/src/app.module.ts new file mode 100644 index 0000000..804a856 --- /dev/null +++ b/src/app.module.ts @@ -0,0 +1,66 @@ +import { join } from "node:path"; +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { ScheduleModule } from "@nestjs/schedule"; +import { ServeStaticModule } from "@nestjs/serve-static"; +import * as dotenv from "dotenv"; +import { CommandModule } from "nestjs-command"; +import { AiModule } from "./ai/ai.module"; +import { AuthModule } from "./auth/auth.module"; +import { ClaimRequestManagementModule } from "./claim-request-management/claim-request-management.module"; +import { ClientModule } from "./client/client.module"; +import { ExpertBlameModule } from "./expert-blame/expert-blame.module"; +import { ExpertClaimModule } from "./expert-claim/expert-claim.module"; +import { ExpertInsurerModule } from "./expert-insurer/expert-insurer.module"; +import { LookupsModule } from "./lookups/lookups.module"; +import { PlatesModule } from "./plates/plates.module"; +import { ProfileModule } from "./profile/profile.module"; +import { SandHubModule } from "./sand-hub/sand-hub.module"; +import { ReportsModule } from "./reports/reports.module"; +import { RequestManagementModule } from "./request-management/request-management.module"; +import { UsersModule } from "./users/users.module"; +import { CronModule } from "./utils/cron/cron.module"; + +dotenv.config(); +dotenv.config({ path: `.${process.env.NODE_ENV}.env` }); + +@Module({ + imports: [ + CommandModule, + ScheduleModule.forRoot(), + CronModule, + ServeStaticModule.forRoot({ + rootPath: join(__dirname, "..", "files"), + serveRoot: "/files", + }), + MongooseModule.forRoot( + `mongodb://${process.env.MONGO_URL}:${process.env.MONGO_PORT}/`, + { + dbName: "yara724", + autoIndex: true, + user: process.env.MONGO_USER, + pass: process.env.MONGO_PASS, + authMechanism: "SCRAM-SHA-256", + tls: true, + tlsAllowInvalidCertificates: true, + }, + ), + UsersModule, + AuthModule, + ClientModule, + ProfileModule, + PlatesModule, + RequestManagementModule, + SandHubModule, + ExpertBlameModule, + ClaimRequestManagementModule, + ExpertClaimModule, + AiModule, + ReportsModule, + ExpertInsurerModule, + LookupsModule, + ], + controllers: [], + providers: [], +}) +export class AppModule {} diff --git a/src/auth/auth-controllers/actor/actor.auth.controller.ts b/src/auth/auth-controllers/actor/actor.auth.controller.ts new file mode 100644 index 0000000..e099ba0 --- /dev/null +++ b/src/auth/auth-controllers/actor/actor.auth.controller.ts @@ -0,0 +1,123 @@ +import { + Body, + Controller, + Get, + Param, + Patch, + Post, + Req, + UseGuards, +} from "@nestjs/common"; +import { + ApiBody, + ApiAcceptedResponse, + ApiResponse, + ApiTags, + ApiBearerAuth, +} from "@nestjs/swagger"; +import { ActorAuthService } from "src/auth/auth-services/actor.auth.service"; +import { + ForgetPasswordSendCodeDto, + ForgetPasswordVerifyCodeDto, +} from "src/auth/dto/actor/forget-password.actor.dto"; +import { LoginActorDto } from "src/auth/dto/actor/login.actor.dto"; +import { ActorEditUserProfileDto } from "src/auth/dto/actor/profile.actor.dto"; +import { + GenuineRegisterDto, + InsurerRegisterDto, + LegalRegisterDto, +} from "src/auth/dto/actor/register.actor.dto"; +import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard"; +import { ClientKey } from "src/decorators/clientKey.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { CurrentUser } from "src/decorators/user.decorator"; + +@Controller("actor") +@ApiTags("actor") +export class ActorAuthController { + constructor(private readonly actorAuthService: ActorAuthService) {} + + @Post("register/genuine") + @ApiBody({ type: GenuineRegisterDto }) + async registerGenuine(@Body() body: GenuineRegisterDto) { + return await this.actorAuthService.genuineRegister(body); + } + + @Post("register/legal") + @ApiBody({ type: LegalRegisterDto }) + async registerLegal(@Body() body: LegalRegisterDto) { + return await this.actorAuthService.legalRegister(body); + } + + @Post("register/insurer") + @ApiBody({ type: InsurerRegisterDto }) + async registerInsurer(@Body() body: InsurerRegisterDto) { + return await this.actorAuthService.insurerRegister(body); + } + + @UseGuards(LocalActorAuthGuard) + @Post("login") + @Roles() + @ApiBody({ + type: LoginActorDto, + description: "user verify otp -- call this api and get a tokens", + }) + @ApiAcceptedResponse() + async login(@Body() body, @Req() req, @ClientKey() client) { + return await this.actorAuthService.loginActors(req.user); + } + + @Post("forget-password") + @ApiBody({ + type: ForgetPasswordSendCodeDto, + description: "send otp when call this api", + }) + @ApiAcceptedResponse() + @ApiResponse({ type: ForgetPasswordSendCodeDto }) + async forgetPassword(@Body() body: ForgetPasswordSendCodeDto) { + return await this.actorAuthService.forgetPasswordSendMail(body.email); + } + + @Post("forget-password-verify") + @ApiBody({ + type: ForgetPasswordVerifyCodeDto, + description: "send otp when call this api", + }) + @ApiAcceptedResponse() + @ApiResponse({ type: ForgetPasswordVerifyCodeDto }) + async forgetPasswordVerify(@Body() body: ForgetPasswordVerifyCodeDto) { + const { email, otp, newPassword } = body; + return await this.actorAuthService.forgetPasswordVerify( + email, + otp, + newPassword, + ); + } + + @Get("register/form/states/list") + async getStates() { + return await this.actorAuthService.getStates(); + } + + @Get("register/form/cities/list/:stateId") + async getCities(@Param("stateId") stateId: number) { + return await this.actorAuthService.getCities(stateId); + } + + @Get("/profile") + @UseGuards(LocalActorAuthGuard) + @ApiBearerAuth() + async getProfile(@CurrentUser() user) { + return await this.actorAuthService.getProfiles(user); + } + + @Patch("/profile") + @UseGuards(LocalActorAuthGuard) + @ApiBearerAuth() + async editProfile( + @CurrentUser() user, + @Body() update: ActorEditUserProfileDto, + ) { + return await this.actorAuthService.modifyProfile(user, update); + } +} diff --git a/src/auth/auth-controllers/user/user.auth.controller.ts b/src/auth/auth-controllers/user/user.auth.controller.ts new file mode 100644 index 0000000..25f5694 --- /dev/null +++ b/src/auth/auth-controllers/user/user.auth.controller.ts @@ -0,0 +1,45 @@ +import { + Body, + Controller, + HttpException, + HttpStatus, + Post, + Req, + UseGuards, +} from "@nestjs/common"; +import { ApiAcceptedResponse, ApiBody, ApiTags } from "@nestjs/swagger"; +import { UserAuthService } from "src/auth/auth-services/user.auth.service"; +import { UserLoginDto } from "src/auth/dto/user/login.dto"; +import { UserVerifyOtp } from "src/auth/dto/user/verify.dto"; +import { LocalUserAuthGuard } from "src/auth/guards/user-local.guard"; +import { CurrentUser } from "src/decorators/user.decorator"; + +@Controller("user") +@ApiTags("user") +export class UserAuthController { + constructor(private readonly userAuthService: UserAuthService) {} + + @Post("/send-otp") + @ApiBody({ + type: UserLoginDto, + description: "user login api -- call this api and send otp", + }) + @ApiAcceptedResponse() + async sendOtpRq(@Body() body: UserLoginDto) { + const res = await this.userAuthService.sendOtpRequest(body.mobile); + if (res) { + throw new HttpException(res, HttpStatus.ACCEPTED); + } + } + + @Post("/login") + @UseGuards(LocalUserAuthGuard) + @ApiBody({ + type: UserVerifyOtp, + description: "user verify otp -- call this api and get a tokens", + }) + @ApiAcceptedResponse() + async login(@Body() body, @Req() req, @CurrentUser() user) { + return await this.userAuthService.login(req.user); + } +} diff --git a/src/auth/auth-services/actor.auth.service.ts b/src/auth/auth-services/actor.auth.service.ts new file mode 100644 index 0000000..a2710e4 --- /dev/null +++ b/src/auth/auth-services/actor.auth.service.ts @@ -0,0 +1,397 @@ +import { readFileSync } from "node:fs"; +import { + BadGatewayException, + BadRequestException, + ForbiddenException, + Injectable, + InternalServerErrorException, + NotFoundException, + UnauthorizedException, +} from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; +import { Types } from "mongoose"; +import { ForgetPasswordVerifyCodeDtoRs } from "src/auth/dto/actor/forget-password.actor.dto"; +import { ProfileActor } from "src/auth/dto/actor/profile.actor.dto"; +import { + InsurerRegisterDto, + RegisterDto, + RegisterDtoRs, +} from "src/auth/dto/actor/register.actor.dto"; +import { StateListDtoRs } from "src/auth/dto/actor/states.dto"; +import { ClientDbService } from "src/client/entities/db-service/client.db.service"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { UserType } from "src/Types&Enums/userType.enum"; +import { InsurerExpertDbService } from "src/users/entities/db-service/insurer-expert.db.service"; +import { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service"; +import { ExpertDbService } from "src/users/entities/db-service/expert.db.service"; +import { HashService } from "src/utils/hash/hash.service"; +import { MailService } from "src/utils/mail/mail.service"; +import { OtpService } from "src/utils/otp/otp.service"; + +function pick(obj: Record, keys: string[]) { + const out: Record = {}; + for (const key of keys) { + if (obj[key] !== undefined) out[key] = obj[key]; + } + return out; +} + +// TODO FIX REGISTER TO ACTOR.SERVICE AND AUTH IN THIS MODULE +@Injectable() +export class ActorAuthService { + constructor( + private readonly jwtService: JwtService, + private readonly hashService: HashService, + private readonly expertDbService: ExpertDbService, + private readonly damageExpertDbService: DamageExpertDbService, + private readonly insurerExpertDbService: InsurerExpertDbService, + private readonly mailService: MailService, + private readonly clientDbService: ClientDbService, + private readonly otpService: OtpService, + ) {} + + // TODO convrt to class for dynamic controller + public async dynamicDbController(role, username, userId?: string) { + let res; + switch (role) { + case RoleEnum.EXPERT: + if (username == null && userId) + res = await this.expertDbService.findOne({ + _id: new Types.ObjectId(userId), + }); + else res = await this.expertDbService.findOne({ email: username }); + break; + case RoleEnum.DAMAGE_EXPERT: + if (username == null && userId) + res = await this.damageExpertDbService.findOne({ + _id: new Types.ObjectId(userId), + }); + else + res = await this.damageExpertDbService.findOne({ email: username }); + break; + case RoleEnum.COMPANY: + res = await this.insurerExpertDbService.findOne({ email: username }); + break; + default: + return null; + } + return res; + } + + async validateActor(username: string, pass: string, role): Promise { + const user = await this.dynamicDbController(role, username); + if (user) { + if (user.role !== role) { + throw new UnauthorizedException("user not assigned to this role"); + } + if (!(await this.hashService.compare(pass, user.password))) { + throw new UnauthorizedException( + "password is incorrect or access Denied", + ); + } else { + return user; + } + } + return null; + } + + async loginActors(user: any) { + let foundedUser = await this.dynamicDbController(user.role, user.username); + if (foundedUser) { + const payload = { + username: foundedUser.username || foundedUser.email, + sub: foundedUser._id, + fullName: + `${foundedUser.firstName || ""} ${foundedUser.lastName || ""}`.trim(), + role: foundedUser.role || "User", + userType: foundedUser.userType || "UserType", + clientKey: foundedUser.clientKey || null, + }; + + const accToken = this.jwtService.sign(payload, { + secret: `${process.env.SECRET}`, + expiresIn: "1h", + }); + + return { + ...payload, + access_token: accToken, + }; + } else { + throw new UnauthorizedException("expert or damage_expert not found"); + } + } + + async registerActors( + body: RegisterDto | InsurerRegisterDto, + userType: UserType, + ) { + const hashPassword = await this.hashService.hash(body.password); + body.password = hashPassword; + + (body as any).userType = userType; + + try { + if ("username" in body) { + if (userType === UserType.INSURER) { + if (!body.clientKey) { + throw new BadRequestException( + "clientKey is required for this user type.", + ); + } + const clientName = await this.clientDbService.find({ + _id: new Types.ObjectId(body.clientKey), + }); + if (!clientName) throw new NotFoundException("Client Not Found"); + } + + const newCompanyPayload = { + email: body.username, + password: body.password, + role: RoleEnum.COMPANY, + userType: (body as any).userType, + clientKey: body.clientKey, + firstName: body.firstName, + lastName: body.lastName, + }; + + return new RegisterDtoRs( + await this.insurerExpertDbService.create(newCompanyPayload), + ); + } else { + if (userType === UserType.LEGAL || userType === UserType.GENUINE) { + if (!body.insuActivityCo) { + throw new BadRequestException( + "insuActivityCo is required for this user type.", + ); + } + const clientName = await this.clientDbService.find({ + _id: body.insuActivityCo, + }); + if (!clientName) throw new NotFoundException("Client Not Found"); + + body.clientKey = body.insuActivityCo; + body.insuActivityCo = clientName.clientName.english; + } + + switch (body.role) { + case RoleEnum.EXPERT: + return new RegisterDtoRs(await this.expertDbService.create(body)); + case RoleEnum.DAMAGE_EXPERT: + return new RegisterDtoRs( + await this.damageExpertDbService.create(body), + ); + default: + throw new BadRequestException( + `Invalid role for this registration type: ${body.role}`, + ); + } + } + } catch (er) { + if (er.code === 11000) { + throw new BadRequestException( + "A user with these details already exists.", + ); + } else if (er.errors) { + const errObjKey = Object.keys(er.errors)[0]; + const { path, kind } = er.errors[errObjKey]; + throw new BadRequestException( + `Validation failed for field '${path}'. Expected a valid ${kind}.`, + ); + } else { + throw er; + } + } + } + + async legalRegister(body: RegisterDto) { + return await this.registerActors(body, UserType.LEGAL); + } + + async genuineRegister(body: RegisterDto) { + return await this.registerActors(body, UserType.GENUINE); + } + + async insurerRegister(body: InsurerRegisterDto) { + Object.assign(body, { role: RoleEnum.COMPANY }); + return await this.registerActors(body, UserType.INSURER); + } + + /// TODO need to seed + async getStates() { + const states = JSON.parse( + readFileSync(`${process.cwd()}/src/static/states.json`, "utf8"), + ); + return new StateListDtoRs(states); + } + + /// TODO need to seeds + async getCities(id: number) { + const cities = JSON.parse( + readFileSync(`${process.cwd()}/src/static/cities.json`, "utf8"), + ); + const citiesList = cities.filter((c) => c.province_id == id); + return new StateListDtoRs(citiesList); + } + + async forgetPasswordSendMail(email: string) { + const normalizedEmail = email.toLowerCase().trim(); + const filter = { email: normalizedEmail }; + + let actor; + + // expert + actor = await this.expertDbService.findOne(filter); + let dbServiceToUpdate = this.expertDbService; + + if (!actor) { + actor = (await this.damageExpertDbService.findOne(filter)) as any; + dbServiceToUpdate = this.damageExpertDbService as any; + } + + if (!actor) { + actor = await this.insurerExpertDbService.findOne(filter); + dbServiceToUpdate = this.insurerExpertDbService as any; + } + + if (!actor) { + throw new NotFoundException("actor not found"); + } + + const otp = this.otpService.create(); + const sendEmail = await this.mailService.sendMail(normalizedEmail, otp); + + if (sendEmail) { + const hashOtp = await this.hashService.hash(otp); + + await dbServiceToUpdate.findOneAndUpdate(filter, { otp: hashOtp }); + + return sendEmail; + } else { + throw new BadGatewayException("mailServer in unavailable"); + } + } + + async forgetPasswordVerify(email, otp, newPassword) { + const userExist = await this.expertDbService.findOne({ email }); + if (!userExist) throw new NotFoundException("user not found"); + const decodeOtp = await this.hashService.compare(otp, userExist.otp); + if (!decodeOtp) throw new UnauthorizedException("otp invalid"); + if (decodeOtp) { + const hashNewPassword = await this.hashService.hash(newPassword); + await this.expertDbService.findOneAndUpdate( + { email }, + { password: hashNewPassword }, + ); + return new ForgetPasswordVerifyCodeDtoRs(userExist, "update password"); + } + } + + async getProfiles(currentUser) { + const userDetail = await this.dynamicDbController( + currentUser.role, + null, + currentUser.sub, + ); + return new ProfileActor(userDetail); + } + + async modifyProfile(currentUser: any, update: Record) { + const role = currentUser?.role; + const userId = currentUser?.sub; + + if (!role || !userId) { + throw new BadRequestException("Invalid actor payload"); + } + + const allowedFieldsByRole: Record = { + expert: [ + "fullName", + "firstName", + "lastName", + "email", + "phone", + "city", + "state", + "bio", + "nationalCode", + "birthDate", + "avatarUrl", + "address", + ], + damage_expert: [ + "fullName", + "email", + "phone", + "companyName", + "city", + "state", + "bio", + "nationalCode", + "birthDate", + "avatarUrl", + "address", + ], + }; + + const allowedFields = allowedFieldsByRole[role]; + if (!allowedFields) { + throw new ForbiddenException("Profile update not allowed for this role"); + } + + const sanitized = Object.fromEntries( + Object.entries(update).filter(([key]) => allowedFields.includes(key)), + ); + + if (Object.keys(sanitized).length === 0) { + throw new BadRequestException("No valid fields to update"); + } + + if (sanitized.birthDate && typeof sanitized.birthDate === "string") { + const parsed = new Date(sanitized.birthDate); + if (isNaN(parsed.getTime())) { + throw new BadRequestException("birthDate must be a valid date"); + } + sanitized.birthDate = parsed; + } + + // fetch user detail (document or plain object) + const document = await this.dynamicDbController(role, null, userId); + + if (!document) throw new NotFoundException("Profile not found"); + + try { + // if it’s a mongoose document (has .save) + if (typeof document.save === "function") { + Object.assign(document, sanitized); + await document.save(); + return document; + } + + // else, update directly via corresponding DB service + switch (role) { + case "expert": + await this.expertDbService.updateOne( + { _id: new Types.ObjectId(userId) }, + { $set: sanitized }, + ); + break; + + case "damage_expert": + await this.damageExpertDbService.updateOne( + { _id: new Types.ObjectId(userId) }, + { $set: sanitized }, + ); + break; + + default: + throw new ForbiddenException("Unsupported role for update"); + } + + // return the fresh document + return await this.dynamicDbController(role, null, userId); + } catch (err) { + throw new InternalServerErrorException("Failed to update profile"); + } + } +} diff --git a/src/auth/auth-services/user.auth.service.ts b/src/auth/auth-services/user.auth.service.ts new file mode 100644 index 0000000..882a565 --- /dev/null +++ b/src/auth/auth-services/user.auth.service.ts @@ -0,0 +1,137 @@ +import { + HttpException, + HttpStatus, + Injectable, + Logger, + NotAcceptableException, + NotFoundException, +} from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; +import { Types } from "mongoose"; +import { LoginDtoRs } from "src/auth/dto/user/login.dto"; +import { UserDbService } from "src/users/entities/db-service/user.db.service"; +import { HashService } from "src/utils/hash/hash.service"; +import { OtpService } from "src/utils/otp/otp.service"; +import { SmsManagerService } from "src/utils/sms-manager/sms-manager.service"; + +// TODO FIX REGISTER TO USER.SERVICE AND AUTH IN THIS MODULE +@Injectable() +export class UserAuthService { + private readonly logger = new Logger(UserAuthService.name); + + constructor( + private readonly jwtService: JwtService, + private readonly userDbService: UserDbService, + private readonly hashService: HashService, + private readonly otpCreator: OtpService, + private readonly smsManagerService: SmsManagerService, + ) {} + + async validateUser(username: string, pass: string): Promise { + const user = await this.userDbService.findOne({ username }); + if (!user) throw new NotFoundException("user not found"); + + const now = new Date().getTime(); + if (user.otp == null) throw new NotAcceptableException("please get otp"); + if (user.otpExpire < now) { + throw new NotAcceptableException("expire otp"); + } + if (await this.hashService.compare(pass, user.otp)) { + return user; + } + return false; + } + + async login(user: any) { + const payload = { + username: user.username, + sub: user.id, + role: "user", + }; + const accToken = this.jwtService.sign(payload, { + secret: `${process.env.SECRET}`, + }); + await this.userDbService.findOneAndUpdate( + { username: user.username }, + { + tokens: { token: accToken }, + otp: null, + }, + ); + return { + userId: user._id, + access_token: accToken, + }; + } + + async sendOtpRequest(mobile: string): Promise { + const userExist = await this.userDbService.findOne({ + mobile, + }); + const otp = this.otpCreator.create(); + const hashOtp = await this.hashService.hash(otp); + if (!userExist) { + await this.smsSender(otp, mobile); + /// create otp request + const newUser = await this.userDbService.createUser({ + mobile, + username: mobile, + otp: hashOtp, + tokens: { + token: "", + rfToken: "", + }, + fullName: "", + nationalCode: "", + lastLogin: new Date(), + clientKey: new Types.ObjectId(), + birthDay: "", + city: "", + address: "", + state: "", + otpExpire: new Date( + new Date().getTime() + +process.env.EXP_OTP_TIME * 60 * 1000, + ).getTime(), + }); + return new LoginDtoRs(newUser); + } + if (userExist) { + await this.smsSender(otp, mobile); + const updateTokens = await this.userDbService.findOneAndUpdate( + { + username: userExist.username, + }, + { + otp: hashOtp, + otpExpire: new Date( + new Date().getTime() + +process.env.EXP_OTP_TIME * 60 * 1000, + ).getTime(), + }, + ); + if (updateTokens) return new LoginDtoRs(userExist); + } + } + + private async smsSender(otp: string, mobile: string) { + return this.smsManagerService + .verifyLookUp({ + token: otp, + template: process.env.AUTH_SMS_TEMPLATE, + receptor: mobile, + }) + .then((smsRes) => { + this.logger.log( + `${"phone : " + mobile + " " + ", status : " + smsRes["return"].status + ", otp : " + otp} `, + ); + }) + .catch((er) => { + this.logger.error( + `${"phone : " + mobile + " " + ", status : " + er["return"].status + ", otp : " + otp} `, + ); + throw new HttpException( + " auth sms send failed", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + }); + } +} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..ab9cb68 --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,42 @@ +import { Module } from "@nestjs/common"; +import { JwtModule, JwtService } from "@nestjs/jwt"; +import { PassportModule } from "@nestjs/passport"; +import { LocalStrategy } from "src/auth/stratregys/local.strategy"; +import { LocalActorStrategy } from "src/auth/stratregys/local-actor.strategy"; +import { ActorAuthController } from "src/auth/auth-controllers/actor/actor.auth.controller"; +import { UserAuthController } from "src/auth/auth-controllers/user/user.auth.controller"; +import { ActorAuthService } from "src/auth/auth-services/actor.auth.service"; +import { UserAuthService } from "src/auth/auth-services/user.auth.service"; +import { ClientModule } from "src/client/client.module"; +import { UsersModule } from "src/users/users.module"; +import { HashModule } from "src/utils/hash/hash.module"; +import { MailModule } from "src/utils/mail/mail.module"; +import { OtpModule } from "src/utils/otp/otp.module"; +import { SmsManagerModule } from "src/utils/sms-manager/sms-manager.module"; + +@Module({ + imports: [ + MailModule, + UsersModule, + ClientModule, + HashModule, + OtpModule, + PassportModule, + SmsManagerModule, + JwtModule.register({ + signOptions: { expiresIn: "1h" }, + global: true, + secret: `${process.env.SECRET}`, + }), + ], + providers: [ + UserAuthService, + ActorAuthService, + LocalStrategy, + LocalActorStrategy, + JwtService, + ], + exports: [LocalStrategy, UserAuthService, ActorAuthService, JwtService], + controllers: [UserAuthController, ActorAuthController], +}) +export class AuthModule {} diff --git a/src/auth/dto/actor/forget-password.actor.dto.ts b/src/auth/dto/actor/forget-password.actor.dto.ts new file mode 100644 index 0000000..09b8a10 --- /dev/null +++ b/src/auth/dto/actor/forget-password.actor.dto.ts @@ -0,0 +1,30 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class ForgetPasswordSendCodeDto { + @ApiProperty({ example: "balali.arash@gmail.com", type: String }) + email: string; +} + +export class ForgetPasswordVerifyCodeDto { + @ApiProperty({ example: "09331009989", type: String }) + email: string; + + @ApiProperty({ type: String, examples: { true: "22222" } }) + otp: string; + + @ApiProperty({ type: String }) + newPassword: string; +} + +export class ForgetPasswordVerifyCodeDtoRs { + @ApiProperty({ example: "balali.arash@gmail.com", type: String }) + email: string; + + @ApiProperty({ type: String, examples: { true: "22222" } }) + message: string; + + constructor(user, message) { + this.email = user.email; + this.message = message; + } +} diff --git a/src/auth/dto/actor/login.actor.dto.ts b/src/auth/dto/actor/login.actor.dto.ts new file mode 100644 index 0000000..75ca1d1 --- /dev/null +++ b/src/auth/dto/actor/login.actor.dto.ts @@ -0,0 +1,41 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +export class LoginActorDto { + @ApiProperty({ example: RoleEnum, type: "array", description: "LOGIN_DTO" }) + role: RoleEnum[]; + + @ApiProperty({}) + username: string; + + @ApiProperty({}) + password: string; +} + +export class LoginActorDtoRs extends LoginActorDto { + private readonly userId; + constructor(userData) { + super(); + this.userId = userData._id; + this.role = userData.role; + this.username = userData.email; + this.clientKey = userData.clientKey; + this.token = userData.token; + this.refreshToken = userData.refreshToken; + } + + @ApiProperty({ type: "string", description: "LOGIN_DTO_RS" }) + fullName: string; + + @ApiProperty({ type: "string", description: "LOGIN_DTO_RS" }) + role: RoleEnum[]; + + @ApiProperty({ type: "string", description: "LOGIN_DTO_RS" }) + token: string; + + @ApiProperty({ type: "string", description: "LOGIN_DTO_RS" }) + refreshToken: string; + + @ApiProperty({ type: "string", description: "LOGIN_DTO_RS" }) + clientKey: string; +} diff --git a/src/auth/dto/actor/profile.actor.dto.ts b/src/auth/dto/actor/profile.actor.dto.ts new file mode 100644 index 0000000..acd7a9a --- /dev/null +++ b/src/auth/dto/actor/profile.actor.dto.ts @@ -0,0 +1,98 @@ +import { ApiProperty, ApiSchema } from "@nestjs/swagger"; +import { IsMobilePhone, IsString, Length } from "class-validator"; +import { DamageExpertModel } from "src/users/entities/schema/damage-expert.schema"; + +export class ProfileActor { + public email: string; + public fullName: string; + public nationalCode: string; + public insuActivityCo: string; + public userType: string; + public mobile: string; + public address: string; + public state: string; + public city: string; + + constructor(Profile: DamageExpertModel) { + this.email = Profile?.email; + this.fullName = Profile?.firstName + " " + Profile?.lastName; + this.nationalCode = Profile?.nationalCode; + this.insuActivityCo = Profile?.insuActivityCo; + this.userType = Profile?.userType; + this.mobile = Profile?.mobile; + this.address = Profile?.address; + this.state = Profile?.state; + this.city = Profile?.city; + } +} + +@ApiSchema({ + name: "Edit Actor Profile", + description: "Body of the request to edit the actor's profile", +}) +export class ActorEditUserProfileDto { + @ApiProperty({ + type: "string", + description: "First name of the actor", + example: "heshmat", + nullable: true, + }) + @IsString() + @Length(2, 40) + firstName?: string; + + @ApiProperty({ + type: "string", + description: "Last name of the actor", + example: "Heshmati", + nullable: true, + }) + @IsString() + @Length(1, 10) + lastName?: string; + + @ApiProperty({ + type: "string", + description: "Mobile phone of the actor", + example: "09123456789", + nullable: true, + }) + @IsMobilePhone("fa-IR") + mobile?: string; + + @ApiProperty({ + type: "string", + description: "تلفن خط ثابت", + example: "02122222222", + nullable: true, + }) + @IsString() + phone?: string; + + @ApiProperty({ + type: "string", + description: "City of the user", + example: "استان", + nullable: true, + }) + @IsString() + city?: string; + + @ApiProperty({ + type: "string", + description: "State of the user", + example: "شهر", + nullable: true, + }) + @IsString() + state?: string; + + @ApiProperty({ + type: "string", + description: "Address of the user", + example: "آدرس", + nullable: true, + }) + @IsString() + address?: string; +} diff --git a/src/auth/dto/actor/register.actor.dto.ts b/src/auth/dto/actor/register.actor.dto.ts new file mode 100644 index 0000000..44d61af --- /dev/null +++ b/src/auth/dto/actor/register.actor.dto.ts @@ -0,0 +1,341 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Exclude } from "class-transformer"; +import { IsEmail } from "class-validator"; +import { Types } from "mongoose"; +import { Degrees } from "src/Types&Enums/degrees.enum"; +import { + ExpertizedAtEnum, + PreviousWorkEnum, + SkillEnum, +} from "src/Types&Enums/damage-expert.enum"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { UserType } from "src/Types&Enums/userType.enum"; + +@Exclude() +export class RegisterDto { + clientKey?: string; + @ApiProperty({ + example: "Soheil", + type: "string", + description: "firstname of actor", + }) + firstName: string; + + @ApiProperty({ + enum: RoleEnum, + examples: RoleEnum, + type: "string", + description: "firstname of actor", + }) + role: RoleEnum; + + userType: UserType; + + @ApiProperty({ + example: "Hajizadeh", + type: "string", + description: "lastname of actor", + }) + lastName: string; + + @ApiProperty({ + example: "4311402422", + type: "string", + description: "nationalCode of actor", + }) + nationalCode: string; + + @ApiProperty({ + example: "dev.callmeskylark@gmail.com", + type: "string", + description: "email of actor", + }) + email: string; + + @ApiProperty({ + example: "123321", + type: "string", + description: "password of actor", + }) + password: string; + + @ApiProperty({ + example: "7522312365495123", + type: "string", + description: "sheba of actor", + }) + sheba?: string; + + @ApiProperty({ + example: "02133564521", + type: "string", + description: "phone of actor", + }) + phone?: string; + + @ApiProperty({ + example: "09226187419", + type: "string", + description: "mobile of actor", + }) + mobile: string; + + @ApiProperty({ + example: "Tehran", + type: "string", + description: "province of actor", + }) + state?: string; + + @ApiProperty({ + example: "Tehran", + type: "string", + description: "city of actor", + }) + city?: string; + + @ApiProperty({ + example: "Gisha , No 7", + type: "string", + description: "address of actor", + }) + address?: string; + + @ApiProperty({ + enum: Degrees, + examples: Degrees, + type: String, + description: "expDegree of actor", + }) + expDegree?: Degrees; + + @ApiProperty({ + example: "5", + type: "number", + description: "insuActivityTime of actor", + }) + insuActivityTime?: number; + + @ApiProperty({ + example: "64fc4978b74d670939b08920", + type: String, + description: "insuActivityCo of actor", + }) + insuActivityCo: string; + + @ApiProperty({ + example: "5", + type: "number", + description: "policeActivityTime of actor", + }) + policeActivityTime?: number; + + @ApiProperty({ + examples: ["headquarters", "queue"], + type: "string", + description: "policeActivityKind of actor", + }) + policeActivityKind?: string; + + @ApiProperty({ + example: "Tehran", + type: String, + description: "policeServicePlace of actor", + }) + policeServicePlace?: string; +} + +export class GenuineRegisterDto extends RegisterDto { + @ApiProperty({ + type: () => ({ + fullName: { type: "string", example: "John Doe" }, + nationalCode: { type: "string", example: "4311402422" }, + dob: { + type: "string", + example: "1990-01-01", + description: "date of birth in ISO string or yyyy-mm-dd", + }, + mobile: { type: "string", example: "09226187419" }, + }), + description: "personal information of damage expert (duplicated with some flat fields)", + required: false, + }) + personalInfo?: { + fullName: string; + nationalCode: string; + dob: string; + mobile: string; + }; + + @ApiProperty({ + type: () => ({ + nationalCard: { + type: "string", + example: "665f0e5ab74d670939b08920", + description: "fileId of uploaded national card", + }, + selfie: { + type: "string", + example: "665f0e5ab74d670939b08921", + description: "fileId of uploaded selfie", + }, + activityCert: { + type: "string", + example: "665f0e5ab74d670939b08922", + description: "fileId of uploaded activity certificate", + }, + }), + description: + "IDs of already uploaded documents in upload module (no files uploaded directly here)", + required: false, + }) + personalDocs?: { + nationalCard?: string; + selfie?: string; + activityCert?: string; + }; + + @ApiProperty({ + type: () => ({ + state: { + type: "number", + example: 8, + description: "state id from /actor/register/form/states/list", + }, + city: { + type: "number", + example: 101, + description: "city id from /actor/register/form/cities/list/:stateId", + }, + expertizedAt: { + type: "array", + items: { enum: Object.values(ExpertizedAtEnum) }, + example: [ExpertizedAtEnum.BADANE, ExpertizedAtEnum.FANI], + description: "one or more expertized fields", + }, + }), + required: false, + }) + workExperience?: { + state: number; + city: number; + expertizedAt: ExpertizedAtEnum[]; + }; + + @ApiProperty({ + type: () => ({ + workingYears: { + type: "number", + example: 5, + }, + casesCount: { + type: "number", + example: 120, + }, + previousWork: { + enum: PreviousWorkEnum, + example: PreviousWorkEnum.INSURANCE_COMPANY, + }, + }), + required: false, + }) + workRecords?: { + workingYears: number; + casesCount: number; + previousWork: PreviousWorkEnum; + }; + + @ApiProperty({ + type: "array", + items: { + type: "string", + enum: Object.values(SkillEnum), + }, + required: false, + description: "one or more skills of the damage expert", + example: [SkillEnum.SMOOTHING, SkillEnum.SCENE_EXPERT], + }) + skills?: SkillEnum[]; + + @ApiProperty({ + type: () => ({ + IBAN: { + type: "string", + example: "IR820540102680020817909002", + }, + cardNum: { + type: "string", + example: "6037991234567890", + }, + accountNum: { + type: "string", + example: "0101234567000", + }, + }), + required: false, + }) + financialInfo?: { + IBAN: string; + cardNum: string; + accountNum: string; + }; +} + +export class LegalRegisterDto implements RegisterDto { + userType: UserType; + @ApiProperty() + firstName: string; + + @ApiProperty() + role: RoleEnum; + + @ApiProperty() + lastName: string; + + @ApiProperty() + nationalCode: string; + + @ApiProperty() + email: string; + + @ApiProperty() + password: string; + + @ApiProperty() + mobile: string; + + @ApiProperty({ + example: "64fc4978b74d670939b08920", + type: String, + description: "insuActivityCo of actor", + }) + insuActivityCo: string; +} + +export class InsurerRegisterDto { + @ApiProperty() + firstName: string; + + @ApiProperty() + lastName: string; + + @ApiProperty({ description: "just submit by Email" }) + @IsEmail() + username: string; + + @ApiProperty() + password: string; + + @ApiProperty({ example: "{saman : 651abe5814ed4bb6ee20cd81}" }) + clientKey: Types.ObjectId; +} + +export class RegisterDtoRs extends RegisterDto { + @ApiProperty({ type: "string", description: "REGISTER_DTO_RS" }) + message: string; + + constructor(registerData) { + super(); + this.message = registerData.message || "ثبت نام با موفقیت انجام شد."; + } +} diff --git a/src/auth/dto/actor/states.dto.ts b/src/auth/dto/actor/states.dto.ts new file mode 100644 index 0000000..30935b7 --- /dev/null +++ b/src/auth/dto/actor/states.dto.ts @@ -0,0 +1,15 @@ +export class StatesDtoRs { + name: string; + id: string; + constructor(states) { + this.name = states.name; + this.id = states.id; + } +} + +export class StateListDtoRs { + list: StatesDtoRs[]; + constructor(statesLis: []) { + this.list = statesLis.map((s) => new StatesDtoRs(s)); + } +} diff --git a/src/auth/dto/user/forget-password.dto.ts b/src/auth/dto/user/forget-password.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/auth/dto/user/login.dto.ts b/src/auth/dto/user/login.dto.ts new file mode 100644 index 0000000..6a8ffc4 --- /dev/null +++ b/src/auth/dto/user/login.dto.ts @@ -0,0 +1,44 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { UserModel } from "src/users/entities/schema/user.schema"; + +export class UserLoginDto { + @ApiProperty({ + example: "09226187419", + type: "string", + description: "User login dto", + }) + mobile: string; +} + +export class LoginDtoRs extends UserModel { + message: string; + @ApiProperty({ + example: "09226187419", + type: "string", + description: "User login dto", + }) + tokens: { token: string; rfToken: string }; + + @ApiProperty({ + example: "09226187419", + type: "string", + description: "User login dto", + }) + userId: string; + + @ApiProperty({ + example: "09226187419", + type: "string", + description: "User login dto", + }) + firstName: string; + + lastName: string; + username: string; + mobile: string; + nationalCode: string; + constructor(loginData) { + super(); + this.mobile = loginData.mobile; + } +} diff --git a/src/auth/dto/user/verify.dto.ts b/src/auth/dto/user/verify.dto.ts new file mode 100644 index 0000000..24428c4 --- /dev/null +++ b/src/auth/dto/user/verify.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class UserVerifyOtp { + @ApiProperty({ + example: "09226187419", + type: "string", + description: "User login dto", + }) + username: string; + + @ApiProperty({ + example: "258567", + type: "string", + description: "User login verify dto", + }) + password: string; +} diff --git a/src/auth/guards/actor-local.guard.ts b/src/auth/guards/actor-local.guard.ts new file mode 100644 index 0000000..9996360 --- /dev/null +++ b/src/auth/guards/actor-local.guard.ts @@ -0,0 +1,63 @@ +import { + ExecutionContext, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; +import { AuthGuard } from "@nestjs/passport"; +import { ActorAuthService } from "src/auth/auth-services/actor.auth.service"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +@Injectable() +export class LocalActorAuthGuard extends AuthGuard("actor") { + constructor( + private readonly actorAuthService: ActorAuthService, + private readonly jwtService: JwtService, + ) { + super(); + } + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + const path = request.url; + + if (!token) { + if (path === "/actor/login") { + const loginData = await this.actorAuthService.loginActors(request.body); + request.user = loginData; + request.identity = request; + return true; + } else { + throw new UnauthorizedException("Token not found"); + } + } + + try { + const payload = await this.jwtService.verifyAsync(token, { + secret: `${process.env.SECRET}`, + }); + + if ( + ![RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT, RoleEnum.COMPANY].includes( + payload.role, + ) + ) { + throw new UnauthorizedException("User role is not authorized"); + } + + request.user = payload; + request.identity = request.user; + } catch { + throw new UnauthorizedException("Invalid token"); + } + + return true; + } + + private extractTokenFromHeader(request: Request): string | undefined { + //@ts-ignore + const [type, token] = request.headers.authorization?.split(" ") ?? []; + return type === "Bearer" ? token : undefined; + } +} diff --git a/src/auth/guards/claim-access.guard.ts b/src/auth/guards/claim-access.guard.ts new file mode 100644 index 0000000..ce40a69 --- /dev/null +++ b/src/auth/guards/claim-access.guard.ts @@ -0,0 +1,127 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, + ForbiddenException, +} from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service"; +import { Types } from "mongoose"; + +/** + * Guard that allows: + * - Users to access their own claim files + * - Experts to access IN_PERSON claim files they initiated + */ +@Injectable() +export class ClaimAccessGuard implements CanActivate { + constructor( + private readonly jwtService: JwtService, + private readonly claimDbService: ClaimRequestManagementDbService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + if (!token) { + throw new UnauthorizedException(); + } + + try { + const payload = await this.jwtService.verifyAsync(token, { + secret: `${process.env.SECRET}`, + }); + + // Allow users to pass through (they will be checked by service methods) + if (payload.role === RoleEnum.USER) { + request.user = payload; + request.identity = request.user; + return true; + } + + // For experts, check if they're accessing an IN_PERSON claim file they initiated + if ( + payload.role === RoleEnum.EXPERT || + payload.role === RoleEnum.DAMAGE_EXPERT + ) { + // Extract claimRequestId from route params + const claimRequestId = + request.params?.claimRequestId || + request.params?.claimRequestID || + request.params?.id; + + if (!claimRequestId) { + // If no claim ID in params, allow access (e.g., for listing endpoints) + // The service will filter appropriately + request.user = payload; + request.actor = payload; + request.identity = payload; + return true; + } + + // Verify expert has access to this specific claim file + const hasAccess = await this.verifyExpertClaimAccess( + claimRequestId, + payload.sub, + ); + + if (!hasAccess) { + throw new ForbiddenException( + "You can only access IN_PERSON claim files that you initiated", + ); + } + + request.user = payload; + request.actor = payload; + request.identity = payload; + return true; + } + + throw new UnauthorizedException("Invalid role"); + } catch (error) { + if (error instanceof ForbiddenException || error instanceof UnauthorizedException) { + throw error; + } + throw new UnauthorizedException(); + } + } + + private async verifyExpertClaimAccess( + claimRequestId: string, + expertId: string, + ): Promise { + try { + const claim = await this.claimDbService.findOne(claimRequestId); + if (!claim) { + return false; + } + + const blameFile = claim.blameFile; + if (!blameFile) { + return false; + } + + // Check if it's an expert-initiated IN_PERSON file + if ( + blameFile.expertInitiated && + blameFile.creationMethod === "IN_PERSON" && + blameFile.initiatedBy + ) { + // Verify the expert is the one who initiated it + return String(blameFile.initiatedBy) === expertId; + } + + return false; + } catch (error) { + return false; + } + } + + private extractTokenFromHeader(request: any): string | undefined { + const [type, token] = request.headers.authorization?.split(" ") ?? []; + return type === "Bearer" ? token : undefined; + } +} + diff --git a/src/auth/guards/global.guard.ts b/src/auth/guards/global.guard.ts new file mode 100644 index 0000000..dd5ebad --- /dev/null +++ b/src/auth/guards/global.guard.ts @@ -0,0 +1,44 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; +import { Request } from "express"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +@Injectable() +export class GlobalGuard implements CanActivate { + constructor(private readonly jwtService: JwtService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + if (!token) { + throw new UnauthorizedException(); + } + try { + const payload = await this.jwtService.verifyAsync(token, { + secret: `${process.env.SECRET}`, + }); + if (payload.role !== RoleEnum.USER) { + console.log( + "🚀 ~ GlobalGuard ~ canActivate ~ request.user.role:", + request.user.role, + ); + throw new UnauthorizedException(); + } + request.user = payload; + request.identity = request.user; + } catch { + throw new UnauthorizedException(); + } + return true; + } + + private extractTokenFromHeader(request: Request): string | undefined { + const [type, token] = request.headers.authorization?.split(" ") ?? []; + return type === "Bearer" ? token : undefined; + } +} diff --git a/src/auth/guards/role.guard.ts b/src/auth/guards/role.guard.ts new file mode 100644 index 0000000..36816b5 --- /dev/null +++ b/src/auth/guards/role.guard.ts @@ -0,0 +1,24 @@ +import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common"; +import { Reflector } from "@nestjs/core"; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private readonly reflector: Reflector) {} + canActivate(context: ExecutionContext): boolean { + // get the roles required + const roles = this.reflector.getAllAndOverride("role", [ + context.getHandler(), + context.getClass(), + ]); + if (!roles) { + return false; + } + const request = context.switchToHttp().getRequest(); + const userRoles = request.user?.role?.split(","); + return this.validateRoles(roles, userRoles); + } + + validateRoles(roles: string[], userRoles: string[]) { + return roles.some((role) => userRoles.includes(role)); + } +} diff --git a/src/auth/guards/user-local.guard.ts b/src/auth/guards/user-local.guard.ts new file mode 100644 index 0000000..7d7e912 --- /dev/null +++ b/src/auth/guards/user-local.guard.ts @@ -0,0 +1,27 @@ +import { + ExecutionContext, + Injectable, + NotAcceptableException, +} from "@nestjs/common"; +import { AuthGuard } from "@nestjs/passport"; +import { UserAuthService } from "src/auth/auth-services/user.auth.service"; + +@Injectable() +export class LocalUserAuthGuard extends AuthGuard("local") { + constructor(private readonly userAuthService: UserAuthService) { + super(); + } + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const { username, password } = request.body; + let isValidUser = await this.userAuthService.validateUser( + username, + password, + ); + if (!isValidUser) { + throw new NotAcceptableException("otp is wrong"); + } + request["user"] = isValidUser; + return true; + } +} diff --git a/src/auth/stratregys/local-actor.strategy.ts b/src/auth/stratregys/local-actor.strategy.ts new file mode 100644 index 0000000..45eee58 --- /dev/null +++ b/src/auth/stratregys/local-actor.strategy.ts @@ -0,0 +1,22 @@ +import { Injectable } from "@nestjs/common"; +import { PassportStrategy } from "@nestjs/passport"; +import { Strategy } from "passport-local"; +import { ActorAuthService } from "src/auth/auth-services/actor.auth.service"; + +@Injectable() +export class LocalActorStrategy extends PassportStrategy(Strategy, "actor") { + constructor(private readonly actorAuthService: ActorAuthService) { + super(); + } + + // async validate(username, password): Promise { + // const user = await this.actorAuthService.validateActor( + // username, + // password, + // ); + // if (!user) { + // throw new UnauthorizedException("user not found"); + // } + // return user; + // } +} diff --git a/src/auth/stratregys/local.strategy.ts b/src/auth/stratregys/local.strategy.ts new file mode 100644 index 0000000..2d6a8bf --- /dev/null +++ b/src/auth/stratregys/local.strategy.ts @@ -0,0 +1,19 @@ +import { Injectable, UnauthorizedException } from "@nestjs/common"; +import { PassportStrategy } from "@nestjs/passport"; +import { Strategy } from "passport-local"; +import { UserAuthService } from "src/auth/auth-services/user.auth.service"; + +@Injectable() +export class LocalStrategy extends PassportStrategy(Strategy) { + constructor(private readonly userAuthService: UserAuthService) { + super(); + } + + async validate(username: string, password: string): Promise { + const user = await this.userAuthService.validateUser(username, password); + if (!user) { + throw new UnauthorizedException("user not found please register"); + } + return user; + } +} diff --git a/src/claim-request-management/claim-request-management.controller.ts b/src/claim-request-management/claim-request-management.controller.ts new file mode 100644 index 0000000..9823fb8 --- /dev/null +++ b/src/claim-request-management/claim-request-management.controller.ts @@ -0,0 +1,483 @@ +import { readFile } from "node:fs/promises"; +import { extname, parse } from "node:path"; +import { + BadRequestException, + Body, + Controller, + Get, + Param, + Patch, + Post, + Put, + Query, + UploadedFile, + UseGuards, + UseInterceptors, +} from "@nestjs/common"; +import { FileInterceptor } from "@nestjs/platform-express"; +import { + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiParam, + ApiQuery, + ApiTags, +} from "@nestjs/swagger"; +import { diskStorage } from "multer"; +import { GlobalGuard } from "src/auth/guards/global.guard"; +import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { Roles } from "src/decorators/roles.decorator"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { ClaimRequestManagementService } from "./claim-request-management.service"; +import { CarDamagePartDto, OtherCarDamagePartDto } from "./dto/car-part.dto"; +import { UserCommentDto } from "./dto/user-comment.dto"; +import { UserObjectionDto } from "./dto/user-objection.dto"; +import { InPersonVisitDto } from "./dto/in-person-visit.dto"; + +@Controller("claim-request-management") +@ApiTags("claim-request-management") +@Roles(RoleEnum.USER, RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT) +@UseGuards(ClaimAccessGuard, RolesGuard) +@ApiBearerAuth() +export class ClaimRequestManagementController { + constructor( + private readonly claimRequestManagementService: ClaimRequestManagementService, + ) {} + + @ApiParam({ name: "blameId" }) + @Post("/:blameId") + async createClaimRequest( + @Param("blameId") requestId: string, + @CurrentUser() user, + ) { + return await this.claimRequestManagementService.createClaimRequest( + requestId, + user.role === RoleEnum.USER ? user.sub : undefined, + user, + ); + } + + @ApiBody({ type: CarDamagePartDto }) + @Patch("/car-part-damage/:claimRequestID") + @ApiParam({ name: "claimRequestID" }) + async carPartDamage( + @Param("claimRequestID") requestId: string, + @Body() body: CarDamagePartDto, + @CurrentUser() user, + ) { + return await this.claimRequestManagementService.selectCarPartDamage( + requestId, + body, + user, + ); + } + + @Get("/car-other-part") + async getCarOtherParts() { + const carOtherPart = await readFile( + `${process.cwd()}/src/static/car-part.json`, + "utf-8", + ); + return carOtherPart; + } + + @ApiBody({ type: OtherCarDamagePartDto }) + @ApiParam({ name: "claimRequestID" }) + @UseInterceptors( + FileInterceptor("file", { + limits: { + fileSize: 10 * 1024 * 1024, + }, + storage: diskStorage({ + destination: "./files/car-green-cards", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `${file.originalname.split(" ")[0]}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @ApiConsumes("multipart/form-data") + @Patch("/car-other-part-damage/:claimRequestID") + async carOtherPartDamage( + @Param("claimRequestID") requestId: string, + @UploadedFile() file, + @Body() body: OtherCarDamagePartDto, + @CurrentUser() user, + ) { + return await this.claimRequestManagementService.selectCarOtherPartDamage( + requestId, + body, + file, + user, + ); + } + + @Get("required-documents-status/:claimRequestID") + @ApiParam({ name: "claimRequestID" }) + async getRequiredDocumentsStatus( + @Param("claimRequestID") requestId: string, + ) { + return await this.claimRequestManagementService.getRequiredDocumentsStatus( + requestId, + ); + } + + @Get("car-part-image-required/:claimRequestID") + @ApiParam({ name: "claimRequestID" }) + async getImageRequired(@Param("claimRequestID") requestId) { + return await this.claimRequestManagementService.getImageRequiredList( + requestId, + ); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + file: { type: "string", format: "binary" }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("file", { + limits: { fileSize: 10 * 1024 * 1024 }, + storage: diskStorage({ + destination: "./files/claim-required-documents/", + filename: (req, file, callback) => { + const extension = extname(file.originalname); + const basename = parse(file.originalname).name; + const unique = Date.now(); + const sanitizedBasename = basename + .replace(/\s/g, "_") + .replace(/[^\w\-_]/g, "") + .substring(0, 50); + const filename = `${sanitizedBasename}-${unique}${extension}`; + callback(null, filename); + }, + }), + }), + ) + @ApiConsumes("multipart/form-data") + @ApiParam({ name: "claimRequestID" }) + @ApiQuery({ + name: "documentType", + enum: [ + "damaged_driving_license_back", + "damaged_driving_license_front", + "damaged_chassis_number", + "damaged_engine_photo", + "damaged_car_card_front", + "damaged_car_card_back", + "damaged_metal_plate", + "guilty_driving_license_front", + "guilty_driving_license_back", + "guilty_car_card_front", + "guilty_car_card_back", + "guilty_metal_plate", + ], + description: "Type of required document to upload", + }) + @Patch("upload-required-document/:claimRequestID") + async uploadRequiredDocument( + @Param("claimRequestID") requestId: string, + @Query("documentType") documentType: string, + @UploadedFile("file") file: Express.Multer.File, + @CurrentUser() user, + ) { + if (!file) { + throw new BadRequestException("File is required"); + } + return await this.claimRequestManagementService.uploadRequiredDocument( + requestId, + documentType as any, + file, + user, + ); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + file: { type: "string", format: "binary" }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("file", { + limits: { fileSize: 10 * 1024 * 1024 }, + storage: diskStorage({ + destination: "./files/car-parts/", + filename: (req, file, callback) => { + const extension = extname(file.originalname); + const basename = parse(file.originalname).name; + const unique = Date.now(); + // Sanitize filename: remove non-ASCII characters and special chars to avoid encoding issues + const sanitizedBasename = basename + .replace(/\s/g, "_") + .replace(/[^\w\-_]/g, "") // Remove all non-word characters except hyphens and underscores + .substring(0, 50); // Limit length + const filename = `${sanitizedBasename}-${unique}${extension}`; + callback(null, filename); + }, + }), + }), + ) + @ApiConsumes("multipart/form-data") + @ApiParam({ name: "claimRequestID" }) + @ApiParam({ + name: "partId", + description: "The ID of the specific car part being photographed.", + }) + @Patch("capture-car-part-damage/:claimRequestID/:partId") + async captureCarPartDamage( + @Param("partId") partId: string, + @Param("claimRequestID") requestId: string, + @UploadedFile("file") file: Express.Multer.File, + ) { + if (!file) { + throw new BadRequestException("Image file is required."); + } + return await this.claimRequestManagementService.setDamageImage( + requestId, + partId, + file, + ); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + file: { type: "string", format: "binary" }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("file", { + limits: { fileSize: 50 * 1024 * 1024 }, + storage: diskStorage({ + destination: "./files/car-capture-videos/", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `claim-video-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @ApiConsumes("multipart/form-data") + @ApiParam({ name: "claimRequestID" }) + @Patch("car-capture/:claimRequestID") + async captureVideoCapture( + @Param("claimRequestID") requestId: string, + @UploadedFile("file") file: Express.Multer.File, + ) { + return await this.claimRequestManagementService.setVideoCapture( + requestId, + file, + ); + } + + @Get("requests/") + async getRequest(@CurrentUser() currentUser) { + return await this.claimRequestManagementService.myRequests(currentUser); + } + + @Get("request/:claimRequestId") + @ApiParam({ name: "claimRequestId" }) + myRequests( + @Param("claimRequestId") requestId: string, + @CurrentUser() user, + ) { + return this.claimRequestManagementService.requestDetails(requestId, user); + } + + @Put("request/reply/:claimRequestId") + @ApiParam({ name: "claimRequestId" }) + @UseInterceptors( + FileInterceptor("file", { + limits: { + fileSize: 10 * 1024 * 1024, + }, + storage: diskStorage({ + destination: "./files/claim-sign", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `${file.originalname.split(" ")[0]}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @ApiBody({ + type: UserCommentDto, + description: "if partId null , you can upload video capture", + }) + @ApiConsumes("multipart/form-data") + @ApiParam({ name: "claimRequestId" }) + async submitReply( + @Param("claimRequestId") requestId, + @Body() body, + @UploadedFile() file: Express.Multer.File, + @CurrentUser() user, + ) { + return await this.claimRequestManagementService.submitUserReply( + requestId, + body, + file, + user, + ); + } + + @Put("request/resend/:claimRequestId/objection") + @ApiParam({ name: "claimRequestId" }) + @ApiConsumes("application/json") + @ApiBody({ + type: UserObjectionDto, + description: "Objection details with optional new parts", + }) + async handleUserObjection( + @Param("claimRequestId") claimRequestId: string, + @Body() userObjectionDto: UserObjectionDto, + ) { + return await this.claimRequestManagementService.handleUserObjectionAndParts( + claimRequestId, + userObjectionDto, + ); + } + + @Patch("request/resend/:claimRequestId") + @ApiConsumes("multipart/form-data") + @ApiParam({ name: "claimRequestId" }) + @ApiQuery({ name: "fields", enum: ["resendDocuments", "resendCarParts"] }) + @ApiQuery({ name: "partId", required: false }) + @ApiQuery({ name: "documentName", required: false }) + @ApiQuery({ name: "side", required: false }) + @ApiBody({ + schema: { + type: "object", + properties: { + file: { + type: "string", + format: "binary", + }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("file", { + storage: diskStorage({ + destination: "./files/claim-resend-documents", + filename: (req, file, callback) => { + const unique = Date.now(); + const ext = extname(file.originalname); + const filename = `${file.originalname.split(" ")[0]}-${unique}${ext}`; + callback(null, filename); + }, + }), + limits: { fileSize: 10 * 1024 * 1024 }, + }), + ) + async uploadDocuments( + @Param("claimRequestId") claimId: string, + @Query() query: string, + @UploadedFile() file: Express.Multer.File, + @CurrentUser() user, + ) { + return await this.claimRequestManagementService.resendFiles( + claimId, + file, + query, + user, + ); + } + + @Patch("request/reply/:claimRequestId/:partId/upload-factor") + @ApiConsumes("multipart/form-data") + @ApiParam({ name: "claimRequestId" }) + @ApiParam({ name: "partId" }) + @ApiBody({ + schema: { + type: "object", + properties: { + file: { + type: "string", + format: "binary", + }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("file", { + storage: diskStorage({ + destination: "./files/claim-factors", + filename: (req, file, callback) => { + const unique = Date.now(); + const filename = `-${unique}-${file.originalname}`; + callback(null, filename); + }, + }), + limits: { fileSize: 10 * 1024 * 1024 }, + }), + ) + async uploadFactorForPart( + @Param("claimRequestId") claimId: string, + @Param("partId") partId: string, + @UploadedFile() file: Express.Multer.File, + @CurrentUser() user, + ) { + return await this.claimRequestManagementService.uploadClaimFactor( + claimId, + partId, + file, + user, + ); + } + + @ApiBody({ type: InPersonVisitDto }) + @ApiParam({ name: "id" }) + @Patch(":id/visit") + async inPersonVisit( + @Param("id") requestId: string, + @Body() body: InPersonVisitDto, + @CurrentUser() actor, + ) { + // Pass the branchId from the body to the service + return await this.claimRequestManagementService.inPersonVisit( + requestId, + body.branchId, + actor, + ); + } + + @Get("branches/:insuranceId") + async insuranceBranches(@Param("insuranceId") insuranceId: string) { + return await this.claimRequestManagementService.retrieveInsuranceBranches( + insuranceId, + ); + } + + @Get("fanavaran-submit/:claimRequestId") + @ApiParam({ name: "claimRequestId" }) + async fanavaranSubmit(@Param("claimRequestId") claimRequestId: string) { + return await this.claimRequestManagementService.fanavaranSubmit( + claimRequestId, + ); + } + + @Post("fanavaran-submit/:claimRequestId") + @ApiParam({ name: "claimRequestId" }) + async submitToFanavaran(@Param("claimRequestId") claimRequestId: string) { + return await this.claimRequestManagementService.submitToFanavaran( + claimRequestId, + ); + } +} diff --git a/src/claim-request-management/claim-request-management.module.ts b/src/claim-request-management/claim-request-management.module.ts new file mode 100644 index 0000000..51a76e9 --- /dev/null +++ b/src/claim-request-management/claim-request-management.module.ts @@ -0,0 +1,88 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { AiModule } from "src/ai/ai.module"; +import { SandHubModule } from "src/sand-hub/sand-hub.module"; +import { RequestManagementModule } from "src/request-management/request-management.module"; +import { UsersModule } from "src/users/users.module"; +import { ClaimRequestManagementController } from "./claim-request-management.controller"; +import { ClaimRequestManagementService } from "./claim-request-management.service"; +import { CarGreenCardDbService } from "./entites/db-service/car-green-card.db.service"; +import { ClaimRequestManagementDbService } from "./entites/db-service/claim-request-management.db.service"; +import { ClaimSignDbService } from "./entites/db-service/claim-sign.db.service"; +import { DamageImageDbService } from "./entites/db-service/damage-image.db.service"; +import { ClaimFactorsImageDbService } from "./entites/db-service/factor-image.db.service"; +import { VideoCaptureDbService } from "./entites/db-service/video-capture.db.service"; +import { ClaimRequiredDocumentDbService } from "./entites/db-service/claim-required-document.db.service"; +import { + CarGreenCardModel, + CarGreenCardSchema, +} from "./entites/schema/car-green-card.schema"; +import { + ClaimRequiredDocument, + ClaimRequiredDocumentSchema, +} from "./entites/schema/claim-required-document.schema"; +import { + ClaimRequestManagementModel, + ClaimRequestManagementSchema, +} from "./entites/schema/claim-request-management.schema"; +import { ClaimSignModel, ClaimSignSchema } from "./entites/schema/claim-sign"; +import { + DamageImageModelSchema, + DamagePartImageModel, +} from "./entites/schema/damage-image-part.schema"; +import { + ClaimFactorsImage, + ClaimFactorsImageSchema, +} from "./entites/schema/factor-image.schema"; +import { + VideoCaptureModel, + VideoCaptureSchema, +} from "./entites/schema/video-capture.schema"; +import { ClientModule } from "src/client/client.module"; +import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard"; +import { JwtModule } from "@nestjs/jwt"; + +@Module({ + imports: [ + UsersModule, + RequestManagementModule, + AiModule, + SandHubModule, + ClientModule, + JwtModule.register({}), + MongooseModule.forFeature([ + { + name: ClaimRequestManagementModel.name, + schema: ClaimRequestManagementSchema, + }, + { name: CarGreenCardModel.name, schema: CarGreenCardSchema }, + { name: DamagePartImageModel.name, schema: DamageImageModelSchema }, + { name: ClaimSignModel.name, schema: ClaimSignSchema }, + { name: ClaimFactorsImage.name, schema: ClaimFactorsImageSchema }, + { name: VideoCaptureModel.name, schema: VideoCaptureSchema }, + { + name: ClaimRequiredDocument.name, + schema: ClaimRequiredDocumentSchema, + }, + ]), + ], + providers: [ + ClaimRequestManagementService, + ClaimRequestManagementDbService, + CarGreenCardDbService, + DamageImageDbService, + ClaimSignDbService, + ClaimFactorsImageDbService, + VideoCaptureDbService, + ClaimRequiredDocumentDbService, + ClaimAccessGuard, + ], + controllers: [ClaimRequestManagementController], + exports: [ + ClaimRequestManagementDbService, + DamageImageDbService, + VideoCaptureDbService, + ClaimRequiredDocumentDbService, + ], +}) +export class ClaimRequestManagementModule {} diff --git a/src/claim-request-management/claim-request-management.service.ts b/src/claim-request-management/claim-request-management.service.ts new file mode 100644 index 0000000..b036b35 --- /dev/null +++ b/src/claim-request-management/claim-request-management.service.ts @@ -0,0 +1,2556 @@ +import { + BadGatewayException, + BadRequestException, + ForbiddenException, + HttpException, + HttpStatus, + Injectable, + Logger, + NotFoundException, +} from "@nestjs/common"; +import { Types } from "mongoose"; +import { v4 as uuidv4 } from "uuid"; +import axios from "axios"; +import { unlink } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { AiService } from "src/ai/ai.service"; +import { buildFileLink } from "src/helpers/urlCreator"; +import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; +import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; +import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; +import { ClaimStepsEnum } from "src/Types&Enums/claim-request-management/steps.enum"; +import { RequestManagementModel } from "src/request-management/entities/schema/request-management.schema"; +import { UserDbService } from "src/users/entities/db-service/user.db.service"; +import { CarDamagePartDto } from "./dto/car-part.dto"; +import { ClaimPartUploadDetail } from "./dto/claim-detail"; +import { CreateClaimRequestDtoRs } from "./dto/create-claim.dto"; +import { ClaimRequestDtoRs } from "./dto/claim-rs-dto"; +import { ImageRequiredDto } from "./dto/image-required.dto"; +import { MyRequestsDto } from "./dto/my-request.dto"; +import { SubmitUserReplyDtoRs } from "./dto/submit-reply.dto"; +import { UserCommentDto } from "./dto/user-comment.dto"; +import { UserObjectionDto } from "./dto/user-objection.dto"; +import { CarGreenCardDbService } from "./entites/db-service/car-green-card.db.service"; +import { ClaimRequestManagementDbService } from "./entites/db-service/claim-request-management.db.service"; +import { ClaimSignDbService } from "./entites/db-service/claim-sign.db.service"; +import { DamageImageDbService } from "./entites/db-service/damage-image.db.service"; +import { ClaimFactorsImageDbService } from "./entites/db-service/factor-image.db.service"; +import { VideoCaptureDbService } from "./entites/db-service/video-capture.db.service"; +import { ClaimRequiredDocumentDbService } from "./entites/db-service/claim-required-document.db.service"; +import { ClaimRequestManagementModel } from "./entites/schema/claim-request-management.schema"; +import { ImageRequiredModel } from "./entites/schema/image-required.schema"; +import { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service"; +import { FactorStatus } from "src/Types&Enums/claim-request-management/factor-status.enum"; +import { BranchDbService } from "src/client/entities/db-service/branch.db.service"; +import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +@Injectable() +export class ClaimRequestManagementService { + private readonly logger = new Logger(ClaimRequestManagementService.name); + + // Authentication URLs and credentials (same as lookups service) + private readonly GET_APP_TOKEN_URL = + "https://insapm.ir/bimeapimanager/api/EITAuthentication/GetAppToken"; + private readonly LOGIN_URL = + "https://insapm.ir/bimeapimanager/api/EITAuthentication/Login"; + private readonly FANAVARAN_SUBMIT_URL = + "https://apimanager.iraneit.com/BimeApiManager/api/BimeApi/v2.0/car/third-party-car-financial-claims"; + + // Authentication credentials + private readonly APP_NAME = "fanhab"; + private readonly SECRET = "5Fa@N#A2B"; + private readonly USERNAME = "fanhabUser"; + private readonly PASSWORD = "Fan#@2U$3er"; + + // API headers + private readonly CORP_ID = "3539"; + private readonly CONTRACT_ID = "263"; + private readonly LOCATION = "100"; + + constructor( + private readonly blameDbService: RequestManagementDbService, + private readonly claimDbService: ClaimRequestManagementDbService, + private readonly carGreenCardDbService: CarGreenCardDbService, + private readonly damageImageDbService: DamageImageDbService, + private readonly userDbService: UserDbService, + private readonly videoCaptureDbService: VideoCaptureDbService, + private readonly claimSignDbService: ClaimSignDbService, + private readonly aiService: AiService, + private readonly claimRequestManagementDbService: ClaimRequestManagementDbService, + private readonly claimFactorsImageDbService: ClaimFactorsImageDbService, + private readonly damageExpertDbService: DamageExpertDbService, + private readonly branchDbService: BranchDbService, + private readonly claimRequiredDocumentDbService: ClaimRequiredDocumentDbService, + ) {} + + private delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + private userDamageDetail(blRequest: RequestManagementModel) { + const { firstPartyDetails: first, secondPartyDetails: second } = blRequest; + switch (blRequest.expertSubmitReply.guiltyUserId) { + case first.firstPartyId: + return { isGuilty: first, isNotGuilty: second }; + case second.secondPartyId: + return { isGuilty: second, isNotGuilty: first }; + } + } + + /** + * Helper method to get the effective user ID for claim operations. + * For experts accessing IN_PERSON claim files, returns the claim file's userId. + * For regular users, returns their own ID. + */ + private async getEffectiveUserId( + claimRequestId: string, + actor: any, + ): Promise { + // If actor is a user, return their ID + if (actor.role === RoleEnum.USER) { + return actor.sub; + } + + // If actor is an expert, verify they're accessing an IN_PERSON claim file + // and return the claim file's userId + if (actor.role === RoleEnum.EXPERT || actor.role === RoleEnum.DAMAGE_EXPERT) { + const claim = await this.claimDbService.findOne(claimRequestId); + if (!claim) { + throw new NotFoundException("Claim file not found"); + } + + const blameFile = claim.blameFile; + if ( + blameFile?.expertInitiated && + blameFile?.creationMethod === "IN_PERSON" && + String(blameFile.initiatedBy) === actor.sub + ) { + // Expert is accessing IN_PERSON file they initiated - use claim's userId + return String(claim.userId); + } + + throw new ForbiddenException( + "Experts can only access IN_PERSON claim files they initiated", + ); + } + + throw new ForbiddenException("Invalid role"); + } + + async createClaimRequest( + requestId: string, + CurrentUserId?: string, + actor?: any, + ) { + try { + const blameRequest = await this.blameDbService.findOne(requestId); + if (!blameRequest) { + throw new NotFoundException("Source blame request not found."); + } + const existingClaimCount = await this.claimDbService.countByFilter({ + "blameFile._id": new Types.ObjectId(requestId), + }); + if (existingClaimCount > 0) { + throw new ForbiddenException( + "A claim request for this blame file already exists.", + ); + } + + const isStatusValid = + blameRequest.blameStatus === ReqBlameStatus.CloseRequest; + + if (!isStatusValid) { + throw new HttpException( + `Blame request must be 'CloseRequest', but is currently '${blameRequest.blameStatus}'.`, + HttpStatus.BAD_REQUEST, + ); + } + + if (!blameRequest.expertSubmitReply) { + throw new HttpException( + "Cannot create a claim file until the expert has submitted a reply.", + HttpStatus.GONE, + ); + } + + // Determine the damaged user (non-guilty party) + const guiltyUserId = String(blameRequest.expertSubmitReply.guiltyUserId); + let damagedUserId: string; + + // For IN_PERSON expert-initiated files, expert creates the claim + // For LINK method or regular files, user creates their own claim + if ( + blameRequest.expertInitiated && + blameRequest.creationMethod === "IN_PERSON" && + actor && + (actor.role === "EXPERT" || actor.role === "DAMAGE_EXPERT") + ) { + // Expert is creating claim for IN_PERSON file + // Verify expert is the initiating expert + if (String(blameRequest.initiatedBy) !== actor.sub) { + throw new ForbiddenException( + "Only the initiating expert can create claim files for IN_PERSON files.", + ); + } + + // Determine damaged user (the one who is NOT guilty) + if ( + String(blameRequest.firstPartyDetails.firstPartyId) === guiltyUserId + ) { + damagedUserId = String( + blameRequest.secondPartyDetails.secondPartyId, + ); + } else { + damagedUserId = String(blameRequest.firstPartyDetails.firstPartyId); + } + } else { + // Regular user flow + if (!CurrentUserId) { + throw new BadRequestException("User ID is required."); + } + if (guiltyUserId === CurrentUserId) { + throw new HttpException( + "You cannot submit a claim file as you are the guilty party.", + HttpStatus.FORBIDDEN, + ); + } + damagedUserId = CurrentUserId; + } + + const userDetail = await this.userDbService.findOne({ + _id: new Types.ObjectId(damagedUserId), + }); + if (!userDetail) { + throw new NotFoundException("User details could not be found."); + } + + const carDetail = + String(blameRequest.firstPartyDetails.firstPartyId) === damagedUserId + ? blameRequest.firstPartyDetails.firstPartyCarDetail + : blameRequest.secondPartyDetails.secondPartyCarDetail; + + const carPlate = + String(blameRequest.firstPartyDetails.firstPartyId) === damagedUserId + ? blameRequest.firstPartyDetails.firstPartyPlate + : blameRequest.secondPartyDetails.secondPartyPlate; + + const newClaimPayload = { + blameFile: blameRequest, + userId: new Types.ObjectId(damagedUserId), + claimStatus: ReqClaimStatus.WaitingForUserCompleted, + steps: [ClaimStepsEnum.CreateClaimFile], + currentStep: ClaimStepsEnum.CreateClaimFile, + nextStep: ClaimStepsEnum.SelectDamagePart, + fullName: userDetail.fullName, + carDetail, + carPlate, + userClientKey: new Types.ObjectId(userDetail.clientKey), + createdAt: new Date(), + }; + + const response = await this.claimDbService.create(newClaimPayload as any); + + return new CreateClaimRequestDtoRs( + response["_id"], + "Claim file created successfully.", + ); + } catch (er) { + this.logger.error(er); + + if (er instanceof HttpException) { + throw er; + } + + if (er.code === 11000) { + throw new HttpException( + "Duplicate claim file reference.", + HttpStatus.CONFLICT, + ); + } + + throw new HttpException( + "An internal server error occurred.", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private findTrueItems(userPartSelect: CarDamagePartDto) { + const trueItems = []; + for (const side in userPartSelect) { + for (const parts in userPartSelect[side]) { + if (userPartSelect[side][parts] === true) { + trueItems.push({ side, part: parts }); + } + } + } + return trueItems; + } + + async setCarPartDamageAndImage(cl, trueItems) { + cl.carPartDamage = trueItems; + const jsonTrueItems = JSON.parse(JSON.stringify(trueItems)); + cl.imageRequired = new ImageRequiredModel(jsonTrueItems); + cl.save(); + return cl.imageRequired; + } + + async selectCarPartDamage( + requestId: string, + userPartSelect: CarDamagePartDto, + actor: any, + ): Promise { + try { + const cl = await this.claimDbService.findOne(requestId); + + if (!cl) { + throw new HttpException("Claim file not found", HttpStatus.NOT_FOUND); + } + + // Verify access: user must own the claim, or expert must be accessing IN_PERSON file + const effectiveUserId = await this.getEffectiveUserId(requestId, actor); + if (String(cl.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + + if (cl.carPartDamage) { + throw new HttpException( + "car damage part has exist", + HttpStatus.CONFLICT, + ); + } + + const trueItems = this.findTrueItems(userPartSelect); + const damageParts = this.setCarPartDamageAndImage(cl, trueItems); + + await this.claimDbService.findAndUpdate(requestId, { + currentStep: ClaimStepsEnum.SelectDamagePart, + nextStep: ClaimStepsEnum.SelectOtherParts, + $push: { + steps: ClaimStepsEnum.SelectDamagePart, + }, + }); + + return damageParts; + } catch (error) { + this.logger.error(error); + throw error; + } + } + + async selectCarOtherPartDamage( + requestId: string, + body: any, // Use your DTO for better type safety + file: Express.Multer.File, + actor: any, + ) { + try { + // 1. Fetch the claim request and perform initial validation + const claimRequest = await this.claimDbService.findOne(requestId); + if (!claimRequest) { + throw new NotFoundException("Claim file not found"); + } + + // Verify access: user must own the claim, or expert must be accessing IN_PERSON file + const effectiveUserId = await this.getEffectiveUserId(requestId, actor); + if (String(claimRequest.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + + if ( + String(claimRequest.blameFile.expertSubmitReply?.guiltyUserId) === + effectiveUserId + ) { + throw new ForbiddenException( + "User access denied. You are the guilty party in the blame file.", + ); + } + + if (!claimRequest.carPlate) { + throw new BadRequestException( + "Cannot validate ownership: Plate information is missing from the claim file.", + ); + } + + // await this.sandHubService.getCarOwnershipInfo( + // claimRequest.carPlate, + // body.nationalCodeOfInsurer, + // ); + + // await this.sandHubService.getShebaValidation( + // body.nationalCodeOfInsurer, + // body.sheba, + // ); + + const greenCard = await this.carGreenCardDbService.create({ + path: file.path, + fileName: file.filename, + claimId: requestId, + }); + if (!greenCard) { + throw new BadGatewayException( + "Failed to save car green card document.", + ); + } + + const updatePayload = { + $set: { + otherParts: JSON.parse(body.otherParts as any), + nationalCodeOfInsurer: body.nationalCodeOfInsurer, + sheba: body.sheba, + carGreenCard: { + path: buildFileLink(greenCard.path), + fileName: greenCard.fileName, + claimId: requestId, + }, + currentStep: ClaimStepsEnum.SelectOtherParts, + nextStep: ClaimStepsEnum.ImageRequired, + claimStatus: ReqClaimStatus.WaitingForUserCompleted, + }, + $push: { + steps: ClaimStepsEnum.SelectOtherParts, + }, + }; + + this.logger.log( + `[CLAIM] Moving to ImageRequired step for claim ${requestId}. Status set to: ${ReqClaimStatus.WaitingForUserCompleted}`, + ); + + const updatedClaim = await this.claimDbService.findByIdAndUpdate( + requestId, + updatePayload, + ); + + // 6. Return the successful response + return new ClaimPartUploadDetail([updatedClaim]); + } catch (error) { + this.logger.error( + `Error in selectCarOtherPartDamage: ${error.message}`, + error.stack, + ); + // Re-throw the specific error from the validation steps or a generic one. + throw error; + } + } + + /** + * Get the list of required document types based on claim type and whether there's a second party car + * For CAR_BODY type with accident with another object (not a car), only require 7 damaged party documents + * Otherwise, require all 12 documents (7 damaged + 5 guilty party) + */ + private getRequiredDocumentTypes(claimRequest: any): ClaimRequiredDocumentType[] { + const allTypes = Object.values(ClaimRequiredDocumentType); + + // Check if it's CAR_BODY type + const isCarBody = claimRequest.blameFile?.type === "CAR_BODY"; + + if (isCarBody) { + // Check if accident is with another object (not a car) + // This can be determined by: + // 1. carBodyForm.carBodyForm.car === false (explicitly set to false) + // 2. OR no secondPartyCarDetail exists (no second party car) + const isAccidentWithOtherObject = + claimRequest.blameFile?.carBodyForm?.carBodyForm?.car === false || + !claimRequest.blameFile?.secondPartyDetails?.secondPartyCarDetail; + + if (isAccidentWithOtherObject) { + this.logger.debug( + `[getRequiredDocumentTypes] CAR_BODY claim with other object detected. ` + + `Requiring only 7 damaged party documents for claim ${claimRequest._id}`, + ); + // Only require damaged party documents (7 documents) + return allTypes.filter((type) => type.startsWith("damaged_")); + } + } + + // Default: require all 12 documents + return allTypes; + } + + async uploadRequiredDocument( + requestId: string, + documentType: ClaimRequiredDocumentType, + file: Express.Multer.File, + actor: any, + ) { + try { + const claimRequest = await this.claimDbService.findOne(requestId); + if (!claimRequest) { + throw new NotFoundException("Claim file not found"); + } + + // Verify access: user must own the claim, or expert must be accessing IN_PERSON file + const effectiveUserId = await this.getEffectiveUserId(requestId, actor); + if (String(claimRequest.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + + if ( + String(claimRequest.blameFile.expertSubmitReply?.guiltyUserId) === + effectiveUserId + ) { + throw new ForbiddenException( + "User access denied. You are the guilty party in the blame file.", + ); + } + + if (claimRequest.currentStep !== ClaimStepsEnum.UploadRequiredDocuments) { + throw new BadRequestException( + `Claim is not in the required documents upload step. Current step: ${claimRequest.currentStep}`, + ); + } + + if (!file) { + throw new BadRequestException("File is required"); + } + + // Check if this document type has already been uploaded + const existingDocuments = await this.claimRequiredDocumentDbService.findAll({ + claimId: new Types.ObjectId(requestId), + documentType: documentType, + }); + + // If document exists, delete the old one first (allow replacement) + if (existingDocuments.length > 0) { + for (const existingDoc of existingDocuments) { + // Delete the file from filesystem + try { + if (existingDoc.path && existsSync(existingDoc.path)) { + await unlink(existingDoc.path); + this.logger.log( + `Deleted old file: ${existingDoc.path} for document type ${documentType}`, + ); + } + } catch (fileError) { + this.logger.warn( + `Failed to delete file ${existingDoc.path}: ${fileError.message}`, + ); + // Continue even if file deletion fails + } + + // Delete from the separate collection + await this.claimRequiredDocumentDbService.delete(existingDoc._id.toString()); + // Remove from claim's requiredDocuments map + await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + $unset: { + [`requiredDocuments.${documentType}`]: "", + }, + }, + ); + } + } + + // Create the document record + const documentRecord = await this.claimRequiredDocumentDbService.create({ + path: file.path, + fileName: file.filename, + claimId: new Types.ObjectId(requestId), + documentType: documentType, + uploadedAt: new Date(), + }); + + // Add document ID to claim's requiredDocuments map using documentType as key + await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + $set: { + [`requiredDocuments.${documentType}`]: documentRecord._id, + }, + }, + ); + + // Check if all required documents have been uploaded + // For CAR_BODY with other object, only require 7 documents instead of 12 + const allRequiredTypes = this.getRequiredDocumentTypes(claimRequest); + const uploadedDocuments = await this.claimRequiredDocumentDbService.findAll({ + claimId: new Types.ObjectId(requestId), + }); + + const uploadedTypes = uploadedDocuments.map((doc) => doc.documentType); + const allUploaded = allRequiredTypes.every((type) => + uploadedTypes.includes(type), + ); + + // If all documents are uploaded, move to next step + if (allUploaded) { + await this.claimDbService.findAndUpdate(requestId, { + $set: { + currentStep: ClaimStepsEnum.UploadRequiredDocuments, + nextStep: ClaimStepsEnum.waitForDamageExpertComment, + claimStatus: ReqClaimStatus.UnChecked, + }, + $push: { + steps: ClaimStepsEnum.UploadRequiredDocuments, + }, + }); + } else { + // Ensure status is set when entering this step (first document upload) + const currentClaim = await this.claimDbService.findOne(requestId); + if (currentClaim?.claimStatus !== ReqClaimStatus.UploadingRequiredDocuments) { + await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + $set: { + currentStep: ClaimStepsEnum.UploadRequiredDocuments, + nextStep: ClaimStepsEnum.UploadRequiredDocuments, + claimStatus: ReqClaimStatus.UploadingRequiredDocuments, + }, + }, + ); + } + } + + return { + message: "Document uploaded successfully", + documentId: documentRecord._id, + documentType: documentType, + allDocumentsUploaded: allUploaded, + nextStep: allUploaded ? ClaimStepsEnum.waitForDamageExpertComment : null, + }; + } catch (error) { + this.logger.error( + `Error in uploadRequiredDocument: ${error.message}`, + error.stack, + ); + throw error; + } + } + + async getRequiredDocumentsStatus(requestId: string) { + try { + const claimRequest = await this.claimDbService.findOne(requestId); + if (!claimRequest) { + throw new NotFoundException("Claim file not found"); + } + + // Get required document types based on claim type + const allRequiredTypes = this.getRequiredDocumentTypes(claimRequest); + const uploadedDocuments = await this.claimRequiredDocumentDbService.findAll({ + claimId: new Types.ObjectId(requestId), + }); + + const uploadedTypes = uploadedDocuments.map((doc) => doc.documentType); + const status = allRequiredTypes.map((type) => { + const doc = uploadedDocuments.find((d) => d.documentType === type); + return { + documentType: type, + uploaded: !!doc, + documentId: doc?._id || null, + fileName: doc?.fileName || null, + uploadedAt: doc?.uploadedAt || null, + }; + }); + + return { + claimId: requestId, + currentStep: claimRequest.currentStep, + nextStep: claimRequest.nextStep, + allUploaded: allRequiredTypes.every((type) => uploadedTypes.includes(type)), + documents: status, + totalRequired: allRequiredTypes.length, + totalUploaded: uploadedDocuments.length, + }; + } catch (error) { + this.logger.error( + `Error in getRequiredDocumentsStatus: ${error.message}`, + error.stack, + ); + throw error; + } + } + + async getImageRequiredList(requestId: string) { + return await new Promise((resolve, reject) => { + this.claimDbService + .findOne(requestId) + .then((cl) => { + if (!cl) { + throw new HttpException( + "Claim File Not Found", + HttpStatus.NOT_FOUND, + ); + } + + // Allow access if: + // 1. Currently in ImageRequired step (currentStep or nextStep is ImageRequired) + // 2. OR has completed ImageRequired and moved to next step (to allow viewing data after completion) + const isInImageRequiredStep = + cl.currentStep === ClaimStepsEnum.ImageRequired || + cl.nextStep === ClaimStepsEnum.ImageRequired; + + const hasCompletedImageRequired = + cl.steps && + Array.isArray(cl.steps) && + cl.steps.includes(ClaimStepsEnum.ImageRequired); + + if (isInImageRequiredStep || hasCompletedImageRequired) { + this.logger.debug( + `[getImageRequiredList] Allowing access for claim ${requestId}. ` + + `currentStep: ${cl.currentStep}, nextStep: ${cl.nextStep}, ` + + `hasCompletedImageRequired: ${hasCompletedImageRequired}`, + ); + if ( + cl.imageRequired.aroundTheCar && + Array.isArray(cl.imageRequired.aroundTheCar) + ) { + cl.imageRequired.aroundTheCar.forEach((carPart) => { + //@ts-ignore + delete carPart?.aiReport; + }); + cl.imageRequired.selectPartOfCar.forEach((part) => { + delete part.aiReport; + }); + } + resolve(new ImageRequiredDto(cl)); + } else { + this.logger.warn( + `[getImageRequiredList] Access denied for claim ${requestId}. ` + + `currentStep: ${cl.currentStep}, nextStep: ${cl.nextStep}, ` + + `steps: ${JSON.stringify(cl.steps)}`, + ); + throw new HttpException( + "This File is Not in ImageRequired Step", + HttpStatus.FORBIDDEN, + ); + } + }) + .catch((er) => { + this.logger.error(er); + if (er instanceof Error) { + reject(er); + } else { + reject(new Error(`Promise rejected with non-error value: ${er}`)); + } + }); + }); + } + + async setImageRequiredList( + requestId: string, + partOfDamageId: string, + fileDetail: any, + ) { + try { + const document = await this.claimDbService.findOneDocument(requestId); + + if (!document) { + throw new HttpException("Claim file not found", HttpStatus.NOT_FOUND); + } + + let result = []; + const imageRequiredKeys = Object.keys(document.imageRequired); + + if (partOfDamageId === undefined) { + if (document.videoCaptureId) { + throw new HttpException("Video already exists", HttpStatus.CONFLICT); + } + + const videoId = await this.createVideoCapture(fileDetail, requestId); + await this.updateClaimWithVideoCapture(requestId, videoId); + throw new HttpException("Video capture uploaded", HttpStatus.ACCEPTED); + } else if (partOfDamageId) { + await this.processDamageImages( + document, + requestId, + partOfDamageId, + fileDetail, + imageRequiredKeys, + result, + ); + result.forEach((item) => { + if (item.aroundTheCar && Array.isArray(item.aroundTheCar)) { + item.aroundTheCar.forEach((carPart) => { + delete carPart.aiReport; + }); + item.selectPartOfCar.forEach((part) => { + delete part.aiReport; + }); + } + }); + } + + return result; + } catch (error) { + this.handleError(error); + } + } + + async setDamageImage( + requestId: string, + partOfDamageId: string, + fileDetail: Express.Multer.File, + ) { + try { + const document = await this.claimDbService.findOneDocument(requestId); + if (!document) { + throw new HttpException("Claim file not found", HttpStatus.NOT_FOUND); + } + if (!fileDetail) { + throw new BadRequestException("Image file is required."); + } + + if ( + document.currentStep !== ClaimStepsEnum.ImageRequired && + document.nextStep !== ClaimStepsEnum.ImageRequired + ) { + throw new BadRequestException( + `Claim is not in the ImageRequired step. Current step: ${document.currentStep}, Status: ${document.claimStatus}`, + ); + } + + const result = []; + const imageRequiredKeys = Object.keys(document.imageRequired); + + // Process images - AI will be processed in background + await this.processDamageImages( + document, + requestId, + partOfDamageId, + fileDetail, + imageRequiredKeys, + result, + ); + + // Clean up the response - remove AI reports since they're processing in background + result.forEach((item) => { + if (item.aroundTheCar && Array.isArray(item.aroundTheCar)) { + item.aroundTheCar.forEach((carPart) => delete carPart.aiReport); + item.selectPartOfCar.forEach((part) => delete part.aiReport); + } + }); + + this.logger.log( + `[setDamageImage] Image uploaded successfully for part ${partOfDamageId}, request ${requestId}. AI processing started in background.`, + ); + + return result; + } catch (error) { + this.handleError(error); + } + } + + async setVideoCapture(requestId: string, fileDetail: Express.Multer.File) { + try { + const document = await this.claimDbService.findOneDocument(requestId); + if (!document) { + throw new HttpException("Claim file not found", HttpStatus.NOT_FOUND); + } + if (!fileDetail) { + throw new BadRequestException("Video file is required."); + } + if (document.videoCaptureId) { + throw new HttpException( + "A video has already been uploaded for this claim.", + HttpStatus.CONFLICT, + ); + } + + const videoId = await this.createVideoCapture(fileDetail, requestId); + await this.updateClaimWithVideoCapture(requestId, videoId); + + return { + statusCode: HttpStatus.ACCEPTED, + message: "Video capture uploaded successfully.", + videoId: videoId, + }; + } catch (error) { + this.handleError(error); + } + } + + private async createVideoCapture(fileDetail: any, requestId: string) { + const videoId = await this.videoCaptureDbService.create({ + path: fileDetail.path, + filName: fileDetail.filename, + claimId: new Types.ObjectId(requestId), + }); + return videoId; + } + + private async updateClaimWithVideoCapture(requestId: string, videoId: any) { + if ( + await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + $set: { videoCaptureId: videoId }, + }, + ) + ) { + throw new HttpException("Video capture uploaded", HttpStatus.ACCEPTED); + } + } + + private async processAiRequestQueue( + tasks: (() => Promise)[], + delayMs = 200, + ): Promise { + for (const task of tasks) { + await task(); + await this.delay(delayMs); + } + } + + /** + * Process AI requests in the background without blocking the response + * This allows users to upload images quickly without waiting for AI processing + */ + private async processAiRequestQueueInBackground( + tasks: (() => Promise)[], + requestId: string, + partId: string, + ): Promise { + if (tasks.length === 0) { + return; + } + + this.logger.log( + `[BACKGROUND AI] Starting background AI processing for ${tasks.length} image(s), part ${partId}, request ${requestId}`, + ); + + // Process in background without blocking + setImmediate(async () => { + try { + await this.processAiRequestQueue(tasks); + this.logger.log( + `[BACKGROUND AI] Completed AI processing for part ${partId}, request ${requestId}`, + ); + } catch (error) { + this.logger.error( + `[BACKGROUND AI] Failed to process AI requests for part ${partId}, request ${requestId}: ${error.message}`, + error.stack, + ); + // Don't throw - we're in background processing + } + }); + } + + private async handleAiSuccess( + response: any, + key: string, + partId: string, + requestId: string, + ): Promise { + this.logger.log(`[STEP 4/4] Processing AI response for part ${partId}, requestId: ${requestId}`); + + // 1. Validate that we have the processed image + if (!response?.downloadLink) { + this.logger.error(`[ERROR] Missing processed image (downloadLink) in AI response`); + this.logger.error(`[ERROR] Part ID: ${partId}, Request ID: ${requestId}`); + this.logger.error(`[ERROR] Response keys: ${JSON.stringify(response ? Object.keys(response) : 'null')}`); + this.logger.error(`[ERROR] Full response: ${JSON.stringify(response, null, 2)}`); + throw new HttpException( + `AI response missing processed image (downloadLink) for part ${partId}`, + HttpStatus.BAD_GATEWAY, + ); + } + + this.logger.log(`[STEP 4/4] Processed image URL: ${response.downloadLink}`); + + // 2. Safely access the nested properties. + const reports = response?.reports; + const reportText = reports?.distinct_damaged_parts_report?.report; + + // 3. Log warning if reports are missing but continue with image + if (!reports) { + this.logger.warn( + `[WARNING] AI response for part ${partId} is missing the 'reports' object, but downloadLink exists.`, + ); + this.logger.warn(`[WARNING] Will save image but without AI report data.`); + } else { + this.logger.log(`[STEP 4/4] AI reports present: ${reports ? 'yes' : 'no'}`); + } + + // 4. Build the update payload. + const updatePayload = { + $set: { + [`imageRequired.${key}.$[elem].aiReport`]: reports || {}, + [`imageRequired.${key}.$[elem].aiImage`]: response.downloadLink, + [`imageRequired.${key}.$[elem].aiReportText`]: reportText || "", + [`imageRequired.aiReportText`]: response.reportsText || "", + }, + }; + + // 5. Update the database using arrayFilters. + try { + const updateResult = await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + updatePayload, + { + arrayFilters: [{ "elem.partId": partId }], + }, + ); + + if (!updateResult) { + this.logger.error(`[ERROR] Database update failed - no document found or update failed`); + this.logger.error(`[ERROR] Request ID: ${requestId}, Part ID: ${partId}, Key: ${key}`); + throw new HttpException( + `Failed to update database with AI results for part ${partId}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + this.logger.log(`[SUCCESS] Successfully updated AI report and image for part ${partId}`); + } catch (dbError) { + this.logger.error(`[ERROR] Database update error for part ${partId}:`); + this.logger.error(`[ERROR] ${dbError.message}`); + this.logger.error(`[ERROR] Stack: ${dbError.stack}`); + throw new HttpException( + `Database update failed: ${dbError.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async retryAiRequest( + newImageDoc: any, + key: string, + partId: string, + requestId: string, + retries = 3, + ): Promise { + const attemptNumber = 4 - retries; // Track which attempt this is + this.logger.log(`[ATTEMPT ${attemptNumber}] Processing AI request for part ${partId}, requestId: ${requestId}`); + this.logger.log(`[ATTEMPT ${attemptNumber}] File path: ${newImageDoc.path}, File name: ${newImageDoc.fileName}`); + + try { + // Use the new, robust AiService to send the request. + // Pass both path and fileName to the AI service + const response = await this.aiService.aiRequestImage({ + path: newImageDoc.path, + fileName: newImageDoc.fileName, + }); + + // If the request succeeds, handle the success. + await this.handleAiSuccess(response, key, partId, requestId); + this.logger.log(`[SUCCESS] AI processing completed successfully for part ${partId}`); + } catch (error) { + // Extract error source from error message if available + const errorMessage = error.message || String(error); + const errorSource = errorMessage.includes('[ERROR]') + ? errorMessage.split('[ERROR]')[1]?.split(']')[0] || 'UNKNOWN' + : 'UNKNOWN'; + + this.logger.error(`[ATTEMPT ${attemptNumber} FAILED] Error source: ${errorSource}`); + this.logger.error(`[ATTEMPT ${attemptNumber} FAILED] Part ID: ${partId}, Request ID: ${requestId}`); + this.logger.error(`[ATTEMPT ${attemptNumber} FAILED] Error: ${errorMessage}`); + + if (retries > 0) { + this.logger.warn( + `⚠️ Retrying AI request for part ${partId}... Attempts left: ${retries - 1}`, + ); + await this.delay(1000); // Wait before retrying + return this.retryAiRequest( + newImageDoc, + key, + partId, + requestId, + retries - 1, + ); + } else { + this.logger.error(`🔴 [FINAL FAILURE] AI request failed after all retries`); + this.logger.error(`🔴 [FINAL FAILURE] Request ID: ${requestId}`); + this.logger.error(`🔴 [FINAL FAILURE] Part ID: ${partId}`); + this.logger.error(`🔴 [FINAL FAILURE] File path: ${newImageDoc.path}`); + this.logger.error(`🔴 [FINAL FAILURE] Error source: ${errorSource}`); + this.logger.error(`🔴 [FINAL FAILURE] Final error: ${errorMessage}`); + if (error.stack) { + this.logger.error(`🔴 [FINAL FAILURE] Stack trace: ${error.stack}`); + } + // Optional: Update the claim status to indicate an AI failure. + // You could throw here if you want to fail the entire operation + throw error; // Re-throw to propagate the error + } + } + } + + private async processDamageImages( + document: any, + requestId: string, + partOfDamageId: string, + fileDetail: any, + imageRequiredKeys: string[], + result: any[], + ) { + const aiRequestQueue = []; + + for (const key of imageRequiredKeys) { + const images = document.imageRequired[key]; + for (const img of images) { + if (img.partId === partOfDamageId) { + const newImageDoc = await this.createDamageImage( + fileDetail, + requestId, + ); + + // Queue AI request for background processing + const aiRequestTask = () => + this.retryAiRequest(newImageDoc, key, partOfDamageId, requestId); + + aiRequestQueue.push(aiRequestTask); + + // Update the document immediately with the uploaded image + await this.updateImageRequiredDocument( + requestId, + key, + partOfDamageId, + newImageDoc, + ); + const updatedDocs = + await this.claimDbService.findOneDocument(requestId); + this.handleImageUpdateResult(result, updatedDocs, requestId); + } + } + } + + // Process AI requests in the background (fire and forget) + // Don't await - let it run asynchronously so user doesn't have to wait + this.processAiRequestQueueInBackground(aiRequestQueue, requestId, partOfDamageId) + .catch((error) => { + // Log errors but don't fail the upload + this.logger.error( + `[BACKGROUND AI] Error processing AI requests for part ${partOfDamageId}, request ${requestId}: ${error.message}`, + error.stack, + ); + }); + } + + private async updateImageRequiredDocument( + requestId, + key, + partOfDamageId, + newImageDoc, + ) { + await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + $set: { + [`imageRequired.${key}.$[elem].upload`]: true, + [`imageRequired.${key}.$[elem].imageId`]: newImageDoc._id, + }, + }, + { + arrayFilters: [{ "elem.partId": partOfDamageId }], + }, + ); + } + + private async createDamageImage(fileDetail, requestId) { + return await this.damageImageDbService.create({ + path: fileDetail.path, + fileName: fileDetail.filename, + claimId: requestId, + }); + } + + private async handleImageUpdateResult(result, updatedDocs, requestId) { + const aroundCarChecked = updatedDocs.imageRequired.aroundTheCar.filter( + (r) => !r.upload, + ); + const selectPartOfCar = updatedDocs.imageRequired.selectPartOfCar.filter( + (r) => !r.upload, + ); + + result.push(updatedDocs.imageRequired); + if (aroundCarChecked.length === 0 && selectPartOfCar.length === 0) { + await this.claimDbService.findOneAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + $push: { + steps: ClaimStepsEnum.ImageRequired, + }, + $set: { + currentStep: ClaimStepsEnum.UploadRequiredDocuments, + nextStep: ClaimStepsEnum.UploadRequiredDocuments, + claimStatus: ReqClaimStatus.UploadingRequiredDocuments, + }, + }, + ); + } + } + + async myRequests(currentUser) { + // For users, return their own claim files + if (currentUser.role === RoleEnum.USER) { + const claimFile = await this.claimDbService.findAllByAnyFilter({ + userId: new Types.ObjectId(currentUser.sub), + }); + if (!claimFile) throw new NotFoundException("claim file not found"); + return new MyRequestsDto(claimFile); + } + + // For experts, return IN_PERSON claim files they initiated + if ( + currentUser.role === RoleEnum.EXPERT || + currentUser.role === RoleEnum.DAMAGE_EXPERT + ) { + const allClaims = await this.claimDbService.findAllByAnyFilter({}); + const expertClaims = allClaims.filter((claim) => { + const blameFile = claim.blameFile; + return ( + blameFile?.expertInitiated && + blameFile?.creationMethod === "IN_PERSON" && + String(blameFile.initiatedBy) === currentUser.sub + ); + }); + if (expertClaims.length === 0) { + throw new NotFoundException("No IN_PERSON claim files found"); + } + return new MyRequestsDto(expertClaims); + } + + throw new ForbiddenException("Invalid role"); + } + + private waitingForUserResendRS(req: ClaimRequestManagementModel) { + if (req.damageExpertResend) { + return new ClaimRequestDtoRs( + req.damageExpertResend as any, + "این درخواست نیاز به ارسال مدارک مجدد دارد ", + ReqClaimStatus.WaitingForUserToResend, + ); + } else { + throw new ForbiddenException("damageExpertResend doesnt exist"); + } + } + + private closeRequestRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "این درخواست بسته شده است ", + ReqClaimStatus.CloseRequest, + ); + } + + private checkAgainRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "در انتظار پاسخ مجدد کارشناس", + ReqClaimStatus.CheckAgain, + ); + } + + private userPendingRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "این پرونده از طرف شما کامل نشده میباشد", + ReqClaimStatus.UserPending, + ); + } + + private reviewRequestRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + `درخواست درحال برسی توسط کارشناس میباشد ${req.unlockTime} - زمان باقی مانده لطفا صبر کنید`, + ReqClaimStatus.ReviewRequest, + ); + } + + private checkedRequestRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "این درخواست بررسی شده است", + ReqClaimStatus.CheckedRequest, + ); + } + + private uncheckedRequestRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "این درخواست در انتظار بررسی می باشد.", + ReqClaimStatus.UnChecked, + ); + } + + private waitingForUserCompletedRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "این درخواست نیازمند تکمیل توسط کاربر می باشد.", + ReqClaimStatus.WaitingForUserCompleted, + ); + } + + private inPersonVisitRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "مراجعه حضوری", + ReqClaimStatus.InPersonVisit, + ); + } + + private responseController(request: ClaimRequestManagementModel) { + switch (request.claimStatus) { + case ReqClaimStatus.WaitingForUserCompleted: + return this.waitingForUserCompletedRS(request); + case ReqClaimStatus.UnChecked: + return this.uncheckedRequestRS(request); + case ReqClaimStatus.CheckedRequest: + return this.checkedRequestRS(request); + case ReqClaimStatus.ReviewRequest: + return this.reviewRequestRS(request); + case ReqClaimStatus.UserPending: + return this.userPendingRS(request); + case ReqClaimStatus.CloseRequest: + return this.closeRequestRS(request); + case ReqClaimStatus.WaitingForUserToResend: + return this.waitingForUserResendRS(request); + case ReqClaimStatus.CheckAgain: + return this.checkAgainRS(request); + case ReqClaimStatus.InPersonVisit: + return this.inPersonVisitRS(request); + case ReqClaimStatus.PendingFactorValidation: + return this.pendingFactorValidationRS(request); + case ReqClaimStatus.FactorRejected: + return this.factorRejectedRS(request); + case ReqClaimStatus.PendingFactorUpload: + return this.pendingFactorUploadRS(request); + case ReqClaimStatus.UploadingRequiredDocuments: + return this.uploadingRequiredDocumentsRS(request); + default: + return "Unknown status"; + } + } + + private pendingFactorUploadRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "منتظر آپلود فاکتور توسط کاربر", + ReqClaimStatus.PendingFactorUpload, + ); + } + + private pendingFactorValidationRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "منتظر تایید فاکتور توسط کارشناس", + ReqClaimStatus.PendingFactorValidation, + ); + } + + private factorRejectedRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "فاکتور ریجکت شده و باید دوباره ارسال شود", + ReqClaimStatus.FactorRejected, + ); + } + + private uploadingRequiredDocumentsRS(req: ClaimRequestManagementModel) { + return new ClaimRequestDtoRs( + req, + "منتظر آپلود مدارک مورد نیاز توسط کاربر", + ReqClaimStatus.UploadingRequiredDocuments, + ); + } + + async requestDetails(requestId: string, user?: any) { + const request = await this.claimDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Claim request not found"); + } + + // Verify access if user is provided + if (user) { + const effectiveUserId = await this.getEffectiveUserId(requestId, user); + if (String(request.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + } + + if (request.imageRequired) { + request.imageRequired.selectPartOfCar.forEach((r) => { + delete r.aiReport; + }); + + request.imageRequired.aroundTheCar.forEach((r) => { + //@ts-ignore + delete r.aiReport; + }); + } + + // Populate factorLink ObjectIds with actual file URLs + await this.populateFactorLinks(request.damageExpertReply); + await this.populateFactorLinks(request.damageExpertReplyFinal); + + return this.responseController(request); + } + + /** + * Converts factorLink ObjectIds to file URLs + */ + private async populateFactorLinks(reply: any): Promise { + if (!reply || !Array.isArray(reply.parts)) { + return; + } + + for (const part of reply.parts) { + if (part.factorLink && Types.ObjectId.isValid(part.factorLink)) { + const factorDoc = await this.claimFactorsImageDbService.findById( + part.factorLink.toString(), + ); + + if (factorDoc) { + part.factorLink = buildFileLink(factorDoc.path); + } + } + } + } + + async submitUserReply( + requestId: string, + body: UserCommentDto, + file?: Express.Multer.File, + actor?: any, + ) { + try { + const request = await this.claimDbService.findOne(requestId); + + if (!this.isValidRequest(request)) { + throw new HttpException("Invalid request", HttpStatus.NOT_ACCEPTABLE); + } + + // Verify access if actor is provided + if (actor) { + const effectiveUserId = await this.getEffectiveUserId(requestId, actor); + if (String(request.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + } + + const finalReply = + request.damageExpertReplyFinal || request.damageExpertReply; + + const replyFieldKey = request.damageExpertReplyFinal + ? "damageExpertReplyFinal" + : "damageExpertReply"; + + const parts = finalReply?.parts || []; + const missingFactors = parts + .filter((p) => p.factorNeeded === true && !p.factorLink) + .map((p) => p.carPartDamage || p.partId); + + if (missingFactors.length > 0) { + throw new HttpException( + `Missing factor images for parts: ${missingFactors.join(", ")}`, + HttpStatus.BAD_REQUEST, + ); + } + + const userComment = { + ...body, + ...(file ? { fileLink: buildFileLink(file.path) } : {}), + }; + + await this.claimDbService.findAndUpdate(requestId, { + $set: { + [`${replyFieldKey}.userComment`]: { + ...userComment, + time: Date.now(), + }, + }, + }); + + if (body.isAccept) { + const { partsFactorDetail, partsNeedFactor } = + await this.processAcceptance(request, file, requestId, body); + + await this.claimDbService.findAndUpdate(requestId, { + $set: { + claimStatus: ReqClaimStatus.CloseRequest, + }, + }); + + return new SubmitUserReplyDtoRs( + request, + partsFactorDetail, + partsNeedFactor, + ); + } else { + this.handleRejection(request, body.isAccept, requestId); + return { requestId, isAccept: body.isAccept }; + } + } catch (error) { + this.handleError(error); + } + } + + private isValidRequest(request: any): boolean { + return ( + request.damageExpertReply && + request.claimStatus === ReqClaimStatus.CheckedRequest + ); + } + + private async processAcceptance( + request: any, + file: any, + requestId: string, + body: UserCommentDto, + ): Promise<{ partsFactorDetail: any[]; partsNeedFactor: boolean }> { + const partsFactorDetail = []; + let partsNeedFactor = false; + + for (let i = 0; i < request.damageExpertReply.parts.length; i++) { + const part = request.damageExpertReply.parts[i]; + + if (!part || part.partCost === undefined) continue; + + if (part.partCost === "FACTOR") { + partsNeedFactor = await this.handleFactorPart( + requestId, + i, + part, + partsFactorDetail, + partsNeedFactor, + ); + } else { + await this.handleNonFactorPart(requestId, file, body.isAccept, part); + } + } + + return { partsFactorDetail, partsNeedFactor }; + } + + private async handleFactorPart( + requestId: string, + i: number, + part: any, + partsFactorDetail: any[], + partsNeedFactor: boolean, + ): Promise { + if (part.needFactor === undefined) { + const uId = uuidv4(); + partsNeedFactor = true; + + await this.claimDbService.findAndUpdate(requestId, { + $set: { + [`damageExpertReply.parts.${i}.needFactor`]: true, + [`damageExpertReply.parts.${i}.partId`]: uId, + }, + }); + + partsFactorDetail.push({ partId: uId, ...part }); + } else { + partsFactorDetail.push(part); + } + + return partsNeedFactor; + } + + private async handleNonFactorPart( + requestId: string, + file: any, + isAccept: boolean, + part: any, + ): Promise { + const sign = await this.claimSignDbService.create(file); + + await this.claimDbService.findAndUpdate(requestId, { + $set: { + "damageExpertReply.userComment.isAccept": isAccept, + "damageExpertReply.userComment.signId": sign["_id"], + }, + }); + } + + private async handleRejection( + request: any, + isAccept: boolean, + requestId: string, + ): Promise { + await this.claimDbService.findAndUpdate(requestId, { + $set: { + "damageExpertReply.userComment.isAccept": isAccept, + "damageExpertReply.userComment.signId": null, + }, + }); + } + + async uploadClaimFactor( + claimId: string, + partId: string, + file: Express.Multer.File, + actor: any, + ) { + try { + if (!file) { + throw new BadRequestException("A factor file is required for upload."); + } + + const claim = await this.claimRequestManagementDbService.findOne(claimId); + if (!claim) { + throw new NotFoundException("Claim not found"); + } + + // Verify access: user must own the claim, or expert must be accessing IN_PERSON file + const effectiveUserId = await this.getEffectiveUserId(claimId, actor); + if (String(claim.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + + const finalReply = + claim.damageExpertReplyFinal || claim.damageExpertReply; + const replyFieldKey = claim.damageExpertReplyFinal + ? "damageExpertReplyFinal" + : "damageExpertReply"; + + if (!finalReply) { + throw new BadRequestException("No expert reply found in this claim."); + } + + const part = finalReply.parts?.find((p) => p.partId === String(partId)); + if (!part) { + throw new BadRequestException( + "The specified part was not found in the expert's reply.", + ); + } + + const factorRecord = await this.claimFactorsImageDbService.create({ + claimId, + partId, + partName: part.carPartDamage, + path: file.path, + fileName: file.filename, + uploadedAt: new Date(), + }); + + const updatedClaim = + await this.claimRequestManagementDbService.findOneAndUpdate( + { + _id: new Types.ObjectId(claimId), + [`${replyFieldKey}.parts.partId`]: partId, + }, + { + $set: { + [`${replyFieldKey}.parts.$.factorLink`]: factorRecord._id, + [`${replyFieldKey}.parts.$.factorStatus`]: FactorStatus.PENDING, + }, + }, + ); + + if (!updatedClaim) { + throw new NotFoundException( + "Could not find the specific part to update in the database.", + ); + } + + await this.checkAndTransitionStatusAfterUpload(claimId); + + return { + message: "Factor uploaded successfully. Awaiting expert validation.", + factorId: factorRecord._id, + url: buildFileLink(file.path), + }; + } catch (error) { + this.logger.error( + `Error during factor upload for claim ${claimId}:`, + error.stack, + ); + + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + "An internal server error occurred during factor upload.", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async checkAndTransitionStatusAfterUpload( + claimId: string, + ): Promise { + // Re-fetch the latest state of the claim + const claim = await this.claimRequestManagementDbService.findOne(claimId); + const finalReply = claim.damageExpertReplyFinal || claim.damageExpertReply; + + if (!finalReply?.parts) { + return; + } + + const requiredFactorParts = finalReply.parts.filter( + (p) => p.factorNeeded === true, + ); + + if (requiredFactorParts.length === 0) { + return; + } + + const allFactorsAreUploaded = requiredFactorParts.every( + (p) => !!p.factorLink, + ); + + if (allFactorsAreUploaded) { + this.logger.log( + `All required factors for claim ${claimId} have been uploaded. Updating status.`, + ); + await this.claimRequestManagementDbService.findByIdAndUpdate(claimId, { + $set: { + claimStatus: ReqClaimStatus.PendingFactorValidation, + }, + }); + } + } + + private async resendDocumentsController(claimFile, query, file) { + if (!Object.keys(query).includes("documentName")) { + throw new HttpException("use documentName", HttpStatus.BAD_REQUEST); + } + + const fileRequired = claimFile.damageExpertResend.resendDocuments; + if (!fileRequired.includes(query.documentName)) { + throw new HttpException("wrong required file", HttpStatus.BAD_REQUEST); + } + + try { + const updated = await this.claimRequestManagementDbService.findAndUpdate( + String(claimFile._id), + { + $push: { + "userResendDocuments.documents": { + [query.documentName]: buildFileLink(file.path), + }, + }, + }, + { new: true }, + ); + + const existingKeys = updated.userResendDocuments.documents.flatMap( + Object.keys, + ); + const allExist = fileRequired.every((key) => existingKeys.includes(key)); + + if (allExist) { + await this.claimRequestManagementDbService.findAndUpdate( + String(claimFile._id), + { $set: { claimStatus: ReqClaimStatus.CheckAgain } }, + ); + } + + return { + allExist, + updated: updated.userResendDocuments.documents, + fileRequired, + }; + } catch (err) { + this.logger.error(err); + return new HttpException(err.message, err.status); + } + } + + private async resendCarParts(claimFile, query, file) { + if (!Object.keys(query).includes("partId")) { + throw new HttpException( + "use resendCarParts query string", + HttpStatus.BAD_REQUEST, + ); + } + + if (!Object.keys(query).includes("side")) { + throw new HttpException( + "side is required for car parts", + HttpStatus.BAD_REQUEST, + ); + } + + const fileRequired = claimFile.damageExpertResend.resendCarParts.map( + (r) => r.partId, + ); + if (!fileRequired.includes(query.partId)) { + throw new HttpException("wrong required file", HttpStatus.BAD_REQUEST); + } + + try { + const fileEntry = { + [query.partId]: { + link: buildFileLink(file.path), + side: query.side, + }, + }; + + const updated = await this.claimRequestManagementDbService.findAndUpdate( + String(claimFile._id), + { $push: { "userResendDocuments.carParts": fileEntry } }, + { new: true }, + ); + + const existingKeys = updated.userResendDocuments.carParts.flatMap( + Object.keys, + ); + const allExist = fileRequired.every((key) => existingKeys.includes(key)); + + if (allExist) { + await this.claimRequestManagementDbService.findAndUpdate( + String(claimFile._id), + { $set: { claimStatus: ReqClaimStatus.CheckAgain } }, + ); + } + + return { + allExist, + updated: updated.userResendDocuments.carParts, + fileRequired, + }; + } catch (err) { + this.logger.error(err); + return new HttpException(err.message, err.status); + } + } + + async resendFiles(claimId, file, query, actor) { + let claimFile = await this.claimRequestManagementDbService.findOne(claimId); + if (!claimFile) { + throw new NotFoundException("Claim file not found"); + } + + // Verify access: user must own the claim, or expert must be accessing IN_PERSON file + const effectiveUserId = await this.getEffectiveUserId(claimId, actor); + if (String(claimFile.userId) !== effectiveUserId) { + throw new ForbiddenException("You do not have access to this claim file"); + } + + switch (query.fields) { + case "resendDocuments": + return this.resendDocumentsController(claimFile, query, file); + case "resendCarParts": + return this.resendCarParts(claimFile, query, file); + default: + return new HttpException("wrong query string", HttpStatus.BAD_REQUEST); + } + } + + async handleUserObjectionAndParts( + claimRequestId: string, + userObjectionDto: UserObjectionDto, + ) { + const claimRequest = await this.claimDbService.findOne(claimRequestId); + if (!claimRequest) { + throw new NotFoundException("Claim request not found"); + } + + if ( + claimRequest.claimStatus !== ReqClaimStatus.WaitingForUserToResend && + claimRequest.claimStatus !== ReqClaimStatus.CheckedRequest + ) { + throw new BadRequestException( + "درخواست شما در این حالت نمی تواند انجام شود", + ); + } + + if (claimRequest.objection) + throw new BadRequestException("Objection already exists!"); + + const updatePayload: any = { + objection: userObjectionDto, + }; + + if (userObjectionDto.newParts?.length) { + const newPartsWithIds = userObjectionDto.newParts.map((part) => ({ + ...part, + partId: part.partId || new Types.ObjectId(), + })); + + updatePayload["newParts"] = newPartsWithIds; + } + + const updated = await this.claimDbService.findAndUpdate( + claimRequestId, + { + ...updatePayload, + claimStatus: ReqClaimStatus.CheckAgain, + }, + { new: true }, + ); + + return updated.objection; + } + + async inPersonVisit(requestId: string, branchId: string, actorDetail: any) { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Claim not found"); + } + + const branch = await this.branchDbService.findById(branchId); + if (!branch) { + throw new NotFoundException(`Branch with ID ${branchId} not found.`); + } + + if (String(request.userClientKey) !== String(branch.clientKey)) { + throw new ForbiddenException( + "This branch does not belong to the insurer of this claim.", + ); + } + + const updated = await this.claimRequestManagementDbService.findAndUpdate( + requestId, + { + claimStatus: ReqClaimStatus.InPersonVisit, + visitLocation: `Branch ${branch.name} at ${branch.address}`, + }, + ); + + await this.damageExpertDbService.updateStats( + actorDetail.sub, + "handled", + requestId, + ); + + return updated; + } + + async retrieveInsuranceBranches(insuranceId: string) { + return await this.branchDbService.findAll(insuranceId); + } + + private handleError(error) { + this.logger.error(error); + throw error; + } + + /** + * Convert Gregorian date to Persian date format (1404/08/13) + */ + private convertToPersianDate(date: Date | string): string { + const d = new Date(date); + const persianDate = new Date(d).toLocaleDateString("fa-IR", { + year: "numeric", + month: "2-digit", + day: "2-digit", + }); + // Convert Persian digits to English and format as YYYY/MM/DD + const persianToEnglish: { [key: string]: string } = { + "۰": "0", + "۱": "1", + "۲": "2", + "۳": "3", + "۴": "4", + "۵": "5", + "۶": "6", + "۷": "7", + "۸": "8", + "۹": "9", + }; + return persianDate + .split("/") + .map((part) => + part + .split("") + .map((char) => persianToEnglish[char] || char) + .join(""), + ) + .join("/"); + } + + /** + * Get time in 24-hour format (HH:mm) from date + */ + private getTime24Hour(date: Date | string): string { + const d = new Date(date); + const hours = d.getHours().toString().padStart(2, "0"); + const minutes = d.getMinutes().toString().padStart(2, "0"); + return `${hours}:${minutes}`; + } + + /** + * Call reverse geocoding API to get address from lat/lon + */ + private async getAddressFromCoordinates( + lat: number, + lon: number, + ): Promise { + try { + const apiKey = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImI5ZDZjMThkNDRjZjc2OWI2Yjk1ODcyMGFjYmEzMmRiN2NhZjg0Zjk4OTRlMjZiZDg0Yzg3YjVlMzhlMTAyZDlkMWYxOGM5NjNmOTk4YjY2In0.eyJhdWQiOiIyMTcxOCIsImp0aSI6ImI5ZDZjMThkNDRjZjc2OWI2Yjk1ODcyMGFjYmEzMmRiN2NhZjg0Zjk4OTRlMjZiZDg0Yzg3YjVlMzhlMTAyZDlkMWYxOGM5NjNmOTk4YjY2IiwiaWF0IjoxNjgwNjA4NTkxLCJuYmYiOjE2ODA2MDg1OTEsImV4cCI6MTY4MzIwMDU5MSwic3ViIjoiIiwic2NvcGVzIjpbImJhc2ljIl19.rTviLd8b5yTHUDa3ODZyva593eMnL0d3XPg3sKkZxMOf_jNIH6lFQyIfbId-wsd1EAdsOdsL3CME_Y8t332PWJbxMNgnEq4Rf2IkClkvkSx6Sb5_4bmlhBM75zw2SmccvgbFUn4xkTOw0FT4vABC2Y3-MKctjMpmO8QOrVULSKt4psrmQhr7hBu7YRDnAAEc6muZ1VpRvdB1kqNKddoSIrfDaq6aDRJ-BNbGRAaFFvP_kH4cgSCKV4dU0TknL3mRKUiVy6_TDkjtzAN8fE2wsdvNo2pGTJPzKFsR2ipgGNTvB__g3bOnVpKsgFXPBH0e_Qa7ff1tZ3VGWy3jRNh9Lg"; + const response = await axios.get( + `https://map.ir/fast-reverse?lat=${lat}&lon=${lon}`, + { + headers: { + accept: "application/json", + "x-api-key": apiKey, + }, + }, + ); + return response.data?.address_compact || ""; + } catch (error) { + this.logger.error("Failed to get address from coordinates", error); + return ""; + } + } + + /** + * Calculate EstimateAmount from damageExpertReply parts + */ + private calculateEstimateAmount(parts: any[]): number { + if (!parts || !Array.isArray(parts) || parts.length === 0) { + return 0; + } + return parts.reduce((sum, part) => { + const totalPayment = part.totalPayment + ? parseFloat(part.totalPayment.toString()) + : 0; + return sum + (isNaN(totalPayment) ? 0 : totalPayment); + }, 0); + } + + /** + * Fanavaran Submit - Map data from claim-request-management and request-management to third-party-car-financial-claims format + */ + async fanavaranSubmit(claimRequestId: string): Promise { + try { + // Query claim-request-management with only needed fields using aggregate for field selection + const claimRequestResult = await this.claimRequestManagementDbService.aggregate([ + { $match: { _id: new Types.ObjectId(claimRequestId) } }, + { + $project: { + blameFile: 1, + damageExpertReply: 1, + damageExpertReplyFinal: 1, + }, + }, + ]); + + if (!claimRequestResult || claimRequestResult.length === 0) { + throw new NotFoundException("Claim request not found"); + } + + const claimRequest = claimRequestResult[0]; + + if (!claimRequest) { + throw new NotFoundException("Claim request not found"); + } + + if (!claimRequest.blameFile || !claimRequest.blameFile._id) { + throw new BadRequestException("Blame file not found in claim request"); + } + + const blameRequestId = claimRequest.blameFile._id.toString(); + + // Query request-management with only needed fields using aggregate for field selection + const blameRequestResult = await this.blameDbService.aggregate([ + { $match: { _id: new Types.ObjectId(blameRequestId) } }, + { + $project: { + expertSubmitReplyFinal: 1, + expertSubmitReply: 1, + createdAt: 1, + firstPartyLocation: 1, + "firstPartyDetails.firstPartyId": 1, + "firstPartyDetails.nationalCodeOfInsurer": 1, + "secondPartyDetails.secondPartyId": 1, + "secondPartyDetails.nationalCodeOfInsurer": 1, + }, + }, + ]); + + if (!blameRequestResult || blameRequestResult.length === 0) { + throw new NotFoundException("Blame request not found"); + } + + const blameRequest = blameRequestResult[0]; + + if (!blameRequest) { + throw new NotFoundException("Blame request not found"); + } + + // Start building the response object + const result: any = { + AccidentCityId: 701, + AccidentReportTypeId: 155, + AccidentVehicleUsedId: 1, + ClaimExpertId: 1589, + CompensationReferenceId: 167, + CulpritLicenceTypeId: 2, + CulpritTypeId: 337, + AccidentCauseId: 6, // Initialize to null, will be set if found + }; + + // AccidentCauseId: from expertSubmitReplyFinal.fields.accidentReason.fanavaran + // If fanavaran is 0, use id instead. First check if expertSubmitReplyFinal exists, if not check expertSubmitReply + if (blameRequest.expertSubmitReplyFinal) { + // expertSubmitReplyFinal exists, use it for AccidentCauseId + const accidentReason = + blameRequest.expertSubmitReplyFinal?.fields?.accidentReason; + const fanavaranValue = accidentReason?.fanavaran; + const idValue = accidentReason?.id; + + if (fanavaranValue !== undefined && fanavaranValue !== null) { + // If fanavaran is 0, use id instead + if (fanavaranValue === 0) { + result.AccidentCauseId = + idValue !== undefined && idValue !== null ? idValue : null; + } else { + result.AccidentCauseId = fanavaranValue; + } + } else { + this.logger.warn( + `[Fanavaran Submit] expertSubmitReplyFinal exists but fanavaran is null or undefined`, + ); + result.AccidentCauseId = null; + } + } else if (blameRequest.expertSubmitReply) { + // expertSubmitReplyFinal is null, use expertSubmitReply + const accidentReason = + blameRequest.expertSubmitReply?.fields?.accidentReason; + const fanavaranValue = accidentReason?.fanavaran; + const idValue = accidentReason?.id; + + if (fanavaranValue !== undefined && fanavaranValue !== null) { + // If fanavaran is 0, use id instead + if (fanavaranValue === 0) { + result.AccidentCauseId = + idValue !== undefined && idValue !== null ? idValue : null; + } else { + result.AccidentCauseId = fanavaranValue; + } + } else { + this.logger.warn( + `[Fanavaran Submit] expertSubmitReply exists but fanavaran is null or undefined`, + ); + result.AccidentCauseId = null; + } + } else { + this.logger.error( + `[Fanavaran Submit] Both expertSubmitReplyFinal and expertSubmitReply are null/undefined`, + ); + result.AccidentCauseId = null; + } + + // AccidentDate: Convert createdAt to Persian date + if (blameRequest.createdAt) { + result.AccidentDate = this.convertToPersianDate(blameRequest.createdAt); + result.AnnouncementDate = result.AccidentDate; + result.DocReceivedDate = result.AccidentDate; + } + + // AccidentTime: Extract time from createdAt in 24-hour format + if (blameRequest.createdAt) { + result.AccidentTime = this.getTime24Hour(blameRequest.createdAt); + } + + // AccidentLocationAddress: Get from reverse geocoding API + if ( + blameRequest.firstPartyLocation?.lat && + blameRequest.firstPartyLocation?.lon + ) { + result.AccidentLocationAddress = await this.getAddressFromCoordinates( + blameRequest.firstPartyLocation.lat, + blameRequest.firstPartyLocation.lon, + ); + } + + // EstimateAmount: Sum of totalPayment from damageExpertReply parts + const damageReply = + claimRequest.damageExpertReplyFinal || claimRequest.damageExpertReply; + if (damageReply?.parts) { + result.EstimateAmount = this.calculateEstimateAmount( + damageReply.parts, + ); + } else { + result.EstimateAmount = 0; + } + + // Keep other fields from template with default values + result.AccidentCulpritId = null; //todo + result.CouponNo = null; + result.CourtArchiveNo = null; + result.CulpritLicenceCityId = null; + result.CulpritLicenceCountryId = null; + result.CulpritLicenceForeignCityName = null; + result.CulpritLicenceIssuDate = "1394/10/13"; //todo + result.CulpritLicenceNo = "1124242"; + result.DamagedCount = 1; + result.DmgAssessorFirstCreationTime = null; + result.EntryDate = null; + result.IsLicenseMatchWithVehicleKind = 1; + result.HasOtherCulprit = 0; + result.IsAccidentOutOfBorder = 0; + result.IsFatalAccident = 0; + result.IsPlaqueChanged = 0; + result.PlaqueCityId = null; + result.PlaqueKindId = null; + result.PlaqueLeftNo = null; + result.PlaqueMiddleCodeId = null; + result.PlaqueNo = null; + result.PlaqueRightNo = null; + result.PlaqueSampleId = null; + result.PlaqueSerial = null; + result.PoliceOfficerId = 1; + result.PoliceReportDesc = null; + result.PoliceReportSeri = null; + result.PoliceReportSerial = null; + result.PolicyId = null; + result.PreviousPolicyEndDate = ""; + result.TrackingCode = null; + result.IsLicenseReplacement = 0; + result.UnknownCulpritCauseId = null; + + // Get PolicyId from external API + try { + const policyId = await this.getPolicyIdFromExternalApi( + blameRequest, + claimRequestId, + ); + if (policyId) { + result.PolicyId = policyId; + } + } catch (error) { + this.logger.error( + `[Fanavaran Submit] Failed to get PolicyId from external API:`, + error, + ); + // Continue without PolicyId if API call fails + } + + return result; + } catch (error) { + this.logger.error("Error in fanavaranSubmit", error); + throw error; + } + } + + /** + * Step 1: Get appToken from GetAppToken API + */ + private async getAppToken(): Promise { + try { + const requestHeaders: any = { + appname: this.APP_NAME, + secret: this.SECRET, + "Content-Length": "0", + }; + delete requestHeaders["Content-Type"]; + + const response = await axios({ + method: "POST", + url: this.GET_APP_TOKEN_URL, + data: "", + headers: requestHeaders, + transformRequest: [ + (data, headers) => { + if (headers) { + delete headers["Content-Type"]; + delete headers["content-type"]; + } + return data; + }, + ], + }); + + const appToken = + response.headers.apptoken || + response.headers.appToken || + response.headers["apptoken"] || + response.headers["appToken"]; + if (!appToken) { + this.logger.error("Failed to get appToken from response headers"); + throw new BadGatewayException("Failed to get appToken from response headers"); + } + + this.logger.log(`Successfully obtained appToken`); + return appToken; + } catch (error) { + this.logger.error("Failed to get appToken", error); + if (axios.isAxiosError(error)) { + const errorMessage = error.response?.data?.Message || + error.response?.data?.message || + error.response?.data || + error.message || + "Failed to get appToken from external API"; + throw new BadGatewayException(errorMessage); + } + throw new BadGatewayException("Failed to get appToken from external API"); + } + } + + /** + * Step 2: Login to get authenticationToken + */ + private async login(appToken: string): Promise { + try { + const requestHeaders: any = { + appToken: appToken, + userName: this.USERNAME, + password: this.PASSWORD, + "Content-Length": "0", + }; + delete requestHeaders["Content-Type"]; + + const response = await axios({ + method: "POST", + url: this.LOGIN_URL, + data: "", + headers: requestHeaders, + transformRequest: [ + (data, headers) => { + if (headers) { + delete headers["Content-Type"]; + delete headers["content-type"]; + } + return data; + }, + ], + }); + + // Check response headers first (authenticationToken is in headers) + const authenticationToken = + response.headers.authenticationtoken || + response.headers.authenticationToken || + response.headers["authenticationtoken"] || + response.headers["authenticationToken"] || + response.data?.authenticationtoken || + response.data?.authenticationToken || + response.data?.authentication_token; + + if (!authenticationToken) { + this.logger.error("Failed to get authenticationToken from response"); + throw new BadGatewayException("Failed to get authenticationToken from response"); + } + + this.logger.log(`Successfully obtained authenticationToken`); + return authenticationToken; + } catch (error) { + this.logger.error("Failed to login", error); + if (axios.isAxiosError(error)) { + const errorMessage = error.response?.data?.Message || + error.response?.data?.message || + error.response?.data || + error.message || + "Failed to login to external API"; + throw new BadGatewayException(errorMessage); + } + throw new BadGatewayException("Failed to login to external API"); + } + } + + /** + * Get PolicyId from external API + */ + private async getPolicyIdFromExternalApi( + blameRequest: any, + claimRequestId: string, + ): Promise { + try { + // Get guiltyUserId from expertSubmitReplyFinal or expertSubmitReply + let guiltyUserId: string | null = null; + if (blameRequest.expertSubmitReplyFinal?.guiltyUserId) { + guiltyUserId = blameRequest.expertSubmitReplyFinal.guiltyUserId; + } else if (blameRequest.expertSubmitReply?.guiltyUserId) { + guiltyUserId = blameRequest.expertSubmitReply.guiltyUserId; + } + + if (!guiltyUserId) { + this.logger.warn( + `[Fanavaran Submit] guiltyUserId not found in expertSubmitReplyFinal or expertSubmitReply`, + ); + return null; + } + + // Find the nationalCodeOfInsurer by matching guiltyUserId with firstPartyId or secondPartyId + let nationalCodeOfInsurer: string | null = null; + + if ( + blameRequest.firstPartyDetails?.firstPartyId?.toString() === + guiltyUserId.toString() + ) { + nationalCodeOfInsurer = + blameRequest.firstPartyDetails?.nationalCodeOfInsurer; + } else if ( + blameRequest.secondPartyDetails?.secondPartyId?.toString() === + guiltyUserId.toString() + ) { + nationalCodeOfInsurer = + blameRequest.secondPartyDetails?.nationalCodeOfInsurer; + } + + if (!nationalCodeOfInsurer) { + this.logger.warn( + `[Fanavaran Submit] nationalCodeOfInsurer not found for guiltyUserId: ${guiltyUserId}`, + ); + return null; + } + + // Get authenticationToken + const appToken = await this.getAppToken(); + const authenticationToken = await this.login(appToken); + + // Call external API to get PolicyId + const policyInquiryUrl = `https://apimanager.iraneit.com/BimeApiManager/api/BimeApi/v2.0/common/Policies/inquiry-my-policies?InsuranceLineId=5&NationalCode=${nationalCodeOfInsurer}`; + + this.logger.log( + `[Fanavaran Submit] Calling policy inquiry API for nationalCode: ${nationalCodeOfInsurer}`, + ); + + const response = await axios.get(policyInquiryUrl, { + headers: { + authenticationToken: authenticationToken, + CorpId: this.CORP_ID, + ContractId: this.CONTRACT_ID, + Location: this.LOCATION, + "Content-Type": "application/json", + }, + }); + + if ( + Array.isArray(response.data) && + response.data.length > 0 && + response.data[0].PolicyId + ) { + const policyId = response.data[0].PolicyId; + this.logger.log( + `[Fanavaran Submit] Successfully retrieved PolicyId: ${policyId}`, + ); + return policyId; + } else { + this.logger.warn( + `[Fanavaran Submit] No PolicyId found in API response`, + ); + return null; + } + } catch (error) { + this.logger.error( + `[Fanavaran Submit] Error getting PolicyId from external API:`, + error, + ); + if (axios.isAxiosError(error)) { + const errorMessage = + error.response?.data?.Message || + error.response?.data?.message || + error.response?.data || + error.message || + "Failed to get PolicyId from external API"; + this.logger.error(`[Fanavaran Submit] Error message: ${errorMessage}`); + } + throw error; + } + } + + /** + * Submit fanavaran data to external API + */ + async submitToFanavaran(claimRequestId: string): Promise { + try { + // First, get the mapped data + this.logger.log(`[Fanavaran Submit] Starting submission for claimRequestId: ${claimRequestId}`); + const fanavaranData = await this.fanavaranSubmit(claimRequestId); + this.logger.log(`[Fanavaran Submit] Mapped data prepared:`, JSON.stringify(fanavaranData, null, 2)); + + // Validate required fields before submission + if (fanavaranData.AccidentCauseId === undefined || fanavaranData.AccidentCauseId === null) { + this.logger.error(`[Fanavaran Submit] CRITICAL: AccidentCauseId is missing or null!`); + this.logger.error(`[Fanavaran Submit] This field is required by the API. Request will likely fail.`); + this.logger.error(`[Fanavaran Submit] Available fields in result:`, Object.keys(fanavaranData)); + } else { + this.logger.log(`[Fanavaran Submit] AccidentCauseId validated: ${fanavaranData.AccidentCauseId}`); + } + + // Step 1: Get appToken + this.logger.log(`[Fanavaran Submit] Step 1: Getting appToken`); + const appToken = await this.getAppToken(); + this.logger.log(`[Fanavaran Submit] AppToken obtained: ${appToken}`); + + // Step 2: Login to get authenticationToken + this.logger.log(`[Fanavaran Submit] Step 2: Logging in to get authenticationToken`); + const authenticationToken = await this.login(appToken); + this.logger.log(`[Fanavaran Submit] AuthenticationToken obtained: ${authenticationToken}`); + + // Step 3: Submit data to Fanavaran API + this.logger.log(`[Fanavaran Submit] Step 3: Submitting data to Fanavaran API`); + this.logger.log(`[Fanavaran Submit] URL: ${this.FANAVARAN_SUBMIT_URL}`); + this.logger.log(`[Fanavaran Submit] Request headers:`, { + authenticationToken: authenticationToken, + CorpId: this.CORP_ID, + ContractId: this.CONTRACT_ID, + Location: this.LOCATION, + "Content-Type": "application/json", + }); + this.logger.log(`[Fanavaran Submit] Request body:`, JSON.stringify(fanavaranData, null, 2)); + + const response = await axios.post( + this.FANAVARAN_SUBMIT_URL, + fanavaranData, + { + headers: { + authenticationToken: authenticationToken, + CorpId: this.CORP_ID, + ContractId: this.CONTRACT_ID, + Location: this.LOCATION, + "Content-Type": "application/json", + }, + }, + ); + + this.logger.log(`[Fanavaran Submit] API Response Status: ${response.status}`); + this.logger.log(`[Fanavaran Submit] API Response Headers:`, JSON.stringify(response.headers, null, 2)); + this.logger.log(`[Fanavaran Submit] API Response Data:`, JSON.stringify(response.data, null, 2)); + this.logger.log("Successfully submitted data to Fanavaran API"); + + // Step 4: Save ClaimNo and Id from response to claim-request-management document + if (response.data) { + const claimNo = response.data.ClaimNo; + const claimId = response.data.Id; + + this.logger.log(`[Fanavaran Submit] Extracted from response - ClaimNo: ${claimNo}, Id: ${claimId}`); + + if (claimNo !== undefined || claimId !== undefined) { + const updateData: any = { + $set: {}, + }; + if (claimNo !== undefined) { + updateData.$set.claimNo = claimNo; + } + if (claimId !== undefined) { + updateData.$set.claimId = claimId; + } + + this.logger.log(`[Fanavaran Submit] Updating document with:`, JSON.stringify(updateData, null, 2)); + await this.claimRequestManagementDbService.findAndUpdate( + claimRequestId, + updateData, + ); + + this.logger.log( + `Updated claim-request-management document with claimNo: ${claimNo}, claimId: ${claimId}`, + ); + } else { + this.logger.warn(`[Fanavaran Submit] No ClaimNo or Id found in response data`); + } + } else { + this.logger.warn(`[Fanavaran Submit] No response data received`); + } + + return response.data; + } catch (error) { + this.logger.error("[Fanavaran Submit] Error submitting to Fanavaran"); + this.logger.error("[Fanavaran Submit] Error type:", error?.constructor?.name); + + if (axios.isAxiosError(error)) { + this.logger.error("[Fanavaran Submit] Axios Error Details:"); + this.logger.error("[Fanavaran Submit] Error Status:", error.response?.status); + this.logger.error("[Fanavaran Submit] Error Status Text:", error.response?.statusText); + this.logger.error("[Fanavaran Submit] Error Response Headers:", JSON.stringify(error.response?.headers, null, 2)); + this.logger.error("[Fanavaran Submit] Error Response Data:", JSON.stringify(error.response?.data, null, 2)); + this.logger.error("[Fanavaran Submit] Request URL:", error.config?.url); + this.logger.error("[Fanavaran Submit] Request Method:", error.config?.method); + this.logger.error("[Fanavaran Submit] Request Headers:", JSON.stringify(error.config?.headers, null, 2)); + this.logger.error("[Fanavaran Submit] Request Data:", JSON.stringify(error.config?.data, null, 2)); + + const errorMessage = error.response?.data?.message || + error.response?.data?.Message || + error.response?.data || + error.message || + "Failed to submit data to Fanavaran API"; + + this.logger.error("[Fanavaran Submit] Extracted Error Message:", errorMessage); + throw new BadGatewayException(errorMessage); + } else { + this.logger.error("[Fanavaran Submit] Non-Axios Error:", error); + throw new BadGatewayException("Failed to submit data to Fanavaran API"); + } + } + } +} diff --git a/src/claim-request-management/dto/car-part.dto.ts b/src/claim-request-management/dto/car-part.dto.ts new file mode 100644 index 0000000..eb8a5a6 --- /dev/null +++ b/src/claim-request-management/dto/car-part.dto.ts @@ -0,0 +1,108 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class MainParts { + @ApiProperty({ default: false }) + backFender: boolean; + + @ApiProperty({ default: false }) + backWheel: boolean; + + @ApiProperty({ default: false }) + backDoor: boolean; + + @ApiProperty({ default: false }) + frontDoor: boolean; + + @ApiProperty({ default: false }) + mirror: boolean; + + @ApiProperty({ default: false }) + frontWheel: boolean; + + @ApiProperty({ default: false }) + frontFender: boolean; + + @ApiProperty({ default: false }) + backWindow: boolean; + + @ApiProperty({ default: false }) + frontWindow: boolean; +} +export class FrontParts { + @ApiProperty({ default: false }) + frontBumper: boolean; + + @ApiProperty({ default: false }) + frontCarWindshield: boolean; + + @ApiProperty({ default: false }) + carHood: boolean; + + @ApiProperty({ default: false }) + leftLight: boolean; + + @ApiProperty({ default: false }) + rightLight: boolean; + + @ApiProperty({ default: false, description: "جلو پنجره " }) + frontGrille: boolean; +} +export class BackParts { + @ApiProperty({ default: false }) + backBumper: boolean; + + @ApiProperty({ default: false }) + carTrunk: boolean; + + @ApiProperty({ default: false }) + backCarWindshield: boolean; + + @ApiProperty({ default: false }) + leftLight: boolean; + + @ApiProperty({ default: false }) + rightLight: boolean; +} +export class TopParts { + @ApiProperty({ default: false }) + roof: boolean; +} +export class CarDamagePartDto { + @ApiProperty({ type: MainParts }) + left: MainParts[]; + + @ApiProperty({ type: MainParts }) + right: MainParts[]; + + @ApiProperty({ type: FrontParts }) + front: FrontParts[]; + + @ApiProperty({ type: BackParts }) + back: BackParts[]; + + @ApiProperty({ type: TopParts }) + top: TopParts[]; +} + +export class OtherCarDamagePartDto { + @ApiProperty({ + format: "array", + description: "please add items of json into array", + example: [{ "حسگر درها": true }], + }) + otherParts: []; + + @ApiProperty({ format: "string" }) + sheba: string; + + @ApiProperty({ format: "string" }) + nationalCodeOfInsurer: string; + + @ApiProperty({ type: "string", format: "binary", required: true }) + file: Express.Multer.File; +} + +export class CaptureCarPartDto { + @ApiProperty({ type: "string", format: "binary", required: true }) + file: Express.Multer.File; +} diff --git a/src/claim-request-management/dto/claim-detail.ts b/src/claim-request-management/dto/claim-detail.ts new file mode 100644 index 0000000..eaae5ca --- /dev/null +++ b/src/claim-request-management/dto/claim-detail.ts @@ -0,0 +1,16 @@ +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; + +export class ClaimPartUploadDetail { + list: any; + constructor(claimFile: ClaimRequestManagementModel[]) { + this.list = claimFile + .map((c) => { + return { + carPartDamage: c.carPartDamage, + carOtherPartDamage: c.otherParts, + greenCardUpload: !!c.carGreenCard.path, + }; + }) + .flat(2); + } +} diff --git a/src/claim-request-management/dto/claim-rs-dto.ts b/src/claim-request-management/dto/claim-rs-dto.ts new file mode 100644 index 0000000..af30a7e --- /dev/null +++ b/src/claim-request-management/dto/claim-rs-dto.ts @@ -0,0 +1,17 @@ +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; +import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; + +export class ClaimRequestDtoRs { + requestDetail: any; + messsage: string; + status: ReqClaimStatus; + constructor( + req: ClaimRequestManagementModel, + message: string, + status: ReqClaimStatus, + ) { + this.requestDetail = req; + this.messsage = message; + this.status = status; + } +} diff --git a/src/claim-request-management/dto/create-claim.dto.ts b/src/claim-request-management/dto/create-claim.dto.ts new file mode 100644 index 0000000..b207cce --- /dev/null +++ b/src/claim-request-management/dto/create-claim.dto.ts @@ -0,0 +1,8 @@ +export class CreateClaimRequestDtoRs { + message: string; + requestId: string; + constructor(requestId: string, message: string) { + this.requestId = requestId; + this.message = message; + } +} diff --git a/src/claim-request-management/dto/image-required.dto.ts b/src/claim-request-management/dto/image-required.dto.ts new file mode 100644 index 0000000..6b2f21f --- /dev/null +++ b/src/claim-request-management/dto/image-required.dto.ts @@ -0,0 +1,8 @@ +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; + +export class ImageRequiredDto { + public list: {} = {}; + constructor(imageModel: ClaimRequestManagementModel) { + this.list = imageModel.imageRequired; + } +} diff --git a/src/claim-request-management/dto/in-person-visit.dto.ts b/src/claim-request-management/dto/in-person-visit.dto.ts new file mode 100644 index 0000000..210bf9f --- /dev/null +++ b/src/claim-request-management/dto/in-person-visit.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsMongoId, IsNotEmpty } from "class-validator"; + +export class InPersonVisitDto { + @ApiProperty({ + example: "60d5ec49e7b2f8001c8e4d2a", + description: "The unique ID of the branch the user is being sent to.", + }) + @IsNotEmpty() + @IsMongoId() + branchId: string; +} diff --git a/src/claim-request-management/dto/my-request.dto.ts b/src/claim-request-management/dto/my-request.dto.ts new file mode 100644 index 0000000..c6fe62f --- /dev/null +++ b/src/claim-request-management/dto/my-request.dto.ts @@ -0,0 +1,23 @@ +import { Types } from "mongoose"; +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; +import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; + +export class MyRequestsDtoList { + status: ReqClaimStatus; + submitDate: string; + numberOfRequest: number; + _id: Types.ObjectId; + constructor(request: ClaimRequestManagementModel) { + this._id = request["_id"]; + this.status = request.claimStatus; + this.submitDate = new Date(request.createdAt).toLocaleString("fa-IR"); + this.numberOfRequest = request.requestNumber; + } +} + +export class MyRequestsDto { + public list = []; + constructor(requests: ClaimRequestManagementModel[]) { + this.list = requests.map((r) => new MyRequestsDtoList(r)); + } +} diff --git a/src/claim-request-management/dto/submit-reply.dto.ts b/src/claim-request-management/dto/submit-reply.dto.ts new file mode 100644 index 0000000..01e3ff2 --- /dev/null +++ b/src/claim-request-management/dto/submit-reply.dto.ts @@ -0,0 +1,19 @@ +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; +import { UserCommentDto } from "src/claim-request-management/dto/user-comment.dto"; + +export class SubmitUserReplyDtoRs { + requestId: string; + partsNeedFactorDetail: object; + partsNeedFactor: boolean; + userComment: UserCommentDto; + constructor( + claim: ClaimRequestManagementModel, + partsFactorDetail?, + partsNeedFactor?, + ) { + this.requestId = claim["_id"]; + this.partsNeedFactorDetail = partsFactorDetail; + this.partsNeedFactor = partsNeedFactor; + this.userComment = claim.damageExpertReply.userComment; + } +} diff --git a/src/claim-request-management/dto/user-comment.dto.ts b/src/claim-request-management/dto/user-comment.dto.ts new file mode 100644 index 0000000..705e279 --- /dev/null +++ b/src/claim-request-management/dto/user-comment.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class UserCommentDto { + @ApiProperty({ type: Boolean }) + isAccept: boolean; + + @ApiProperty({ type: "string", format: "binary", required: false }) + file?: Express.Multer.File; + + @ApiProperty({ type: String }) + branch?: string; +} diff --git a/src/claim-request-management/dto/user-objection.dto.ts b/src/claim-request-management/dto/user-objection.dto.ts new file mode 100644 index 0000000..fd36c2a --- /dev/null +++ b/src/claim-request-management/dto/user-objection.dto.ts @@ -0,0 +1,47 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { TypeOfDamage } from "src/Types&Enums/claim-request-management/type-of-damage.enum"; + +export class UserObjectionPartDto { + @ApiProperty() + partId: string; + + @ApiProperty({ required: false }) + reason?: string; + + @ApiProperty({ required: false }) + partPrice?: string; + + @ApiProperty({ required: false }) + partSalary?: string; + + @ApiProperty({ required: false }) + typeOfDamage?: TypeOfDamage; + + @ApiProperty({ required: false }) + carPartDamage?: string; + + @ApiProperty({ required: false }) + side?: string; +} + +export class NewPartDto { + @ApiProperty({ required: false, nullable: true }) + partId: string | null; + + @ApiProperty() + partName: string; + + @ApiProperty({ required: false }) + side?: string; +} + +export class UserObjectionDto { + @ApiProperty({ type: [UserObjectionPartDto], required: false }) + @Type(() => UserObjectionPartDto) + objectionParts?: UserObjectionPartDto[]; + + @ApiProperty({ type: [NewPartDto], required: false }) + @Type(() => NewPartDto) + newParts?: NewPartDto[]; +} diff --git a/src/claim-request-management/dto/user-reply.dto.ts b/src/claim-request-management/dto/user-reply.dto.ts new file mode 100644 index 0000000..88eba59 --- /dev/null +++ b/src/claim-request-management/dto/user-reply.dto.ts @@ -0,0 +1,4 @@ +export class UserReplyDtoRs { + requestId: string; + isAccept: string; +} diff --git a/src/claim-request-management/entites/db-service/car-green-card.db.service.ts b/src/claim-request-management/entites/db-service/car-green-card.db.service.ts new file mode 100644 index 0000000..4a10162 --- /dev/null +++ b/src/claim-request-management/entites/db-service/car-green-card.db.service.ts @@ -0,0 +1,19 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model } from "mongoose"; +import { CarGreenCardModel } from "src/claim-request-management/entites/schema/car-green-card.schema"; + +export class CarGreenCardDbService { + constructor( + @InjectModel(CarGreenCardModel.name) + private readonly model: Model, + ) {} + async create(greenCard): Promise { + return await this.model.create(greenCard); + } + + async findOne( + filter: FilterQuery, + ): Promise { + return await this.model.findOne({ filter }); + } +} diff --git a/src/claim-request-management/entites/db-service/claim-request-management.db.service.ts b/src/claim-request-management/entites/db-service/claim-request-management.db.service.ts new file mode 100644 index 0000000..00bc32b --- /dev/null +++ b/src/claim-request-management/entites/db-service/claim-request-management.db.service.ts @@ -0,0 +1,121 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, Types, UpdateQuery } from "mongoose"; +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; +const crypto = require("node:crypto"); + +@Injectable() +export class ClaimRequestManagementDbService { + constructor( + @InjectModel(ClaimRequestManagementModel.name) + private readonly model: Model, + ) {} + + async create( + claimRequest: ClaimRequestManagementModel, + ): Promise { + const uniqueRequestNumber = await this.generateUniqueNumbers(); + return this.model.create({ + ...claimRequest, + requestNumber: uniqueRequestNumber, + }); + } + + async findOne( + id?: string, + filter?: FilterQuery, + ) { + if (filter) return await this.model.findOne(filter); + return await this.model.findOne({ _id: new Types.ObjectId(id) }); + } + + async findOneDocument( + id: string, + filter?: FilterQuery, + ) { + if (filter) return await this.model.findOne(filter); + return await this.model.findOne({ _id: new Types.ObjectId(id) }).lean(); + } + + async findOneAndUpdate( + filter: FilterQuery, + update: UpdateQuery, + option?, + ) { + return await this.model.findOneAndUpdate(filter, update, option); + } + + async findAllByStatus(filter: FilterQuery) { + return await this.model.find(filter); + } + + async findAndDelete( + filter: FilterQuery, + option, + ) { + return await this.model.deleteMany(filter, option); + } + + async findAllAndPagination( + filter: FilterQuery, + currentPage: number, + countPerPage: number, + ) { + const responsePerPage = countPerPage | 1; + const skipPge = responsePerPage * (Number(currentPage) - 1); + return await this.model.find(filter).limit(responsePerPage).skip(skipPge); + } + + async aggregate(filter?) { + return await this.model.aggregate(filter); + } + + async findAndUpdate( + id: string, + update: UpdateQuery, + option?: FilterQuery, + ) { + return await this.model.findByIdAndUpdate( + { _id: new Types.ObjectId(id) }, + update, + option, + ); + } + + async findAllByAnyFilter( + filter: FilterQuery, + ): Promise { + return await this.model.find(filter); + } + + async countByFilter( + filter: FilterQuery, + ): Promise { + return await this.model.countDocuments(filter); + } + async findAll(): Promise { + return await this.model.find(); + } + async generateUniqueNumbers(digits = 5) { + try { + const max = Math.pow(10, digits); + const randomBytes = crypto.randomBytes(Math.ceil(digits / 2)); + const randomNumber = parseInt(randomBytes.toString("hex"), 16) % max; + return randomNumber.toString().padStart(digits, "0"); + } catch (error) { + console.error("Error generating unique numbers:", error); + throw error; + } + } + + async findAllWithFilter(filter?: FilterQuery) { + return await this.model.find(filter); + } + + async findByIdAndUpdate( + id: string, + updateDto: UpdateQuery, + ): Promise { + return this.model.findByIdAndUpdate(id, updateDto, { new: true }).lean(); + } +} diff --git a/src/claim-request-management/entites/db-service/claim-required-document.db.service.ts b/src/claim-request-management/entites/db-service/claim-required-document.db.service.ts new file mode 100644 index 0000000..73b8250 --- /dev/null +++ b/src/claim-request-management/entites/db-service/claim-required-document.db.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, Types } from "mongoose"; +import { ClaimRequiredDocument } from "src/claim-request-management/entites/schema/claim-required-document.schema"; + +@Injectable() +export class ClaimRequiredDocumentDbService { + constructor( + @InjectModel(ClaimRequiredDocument.name) + private readonly model: Model, + ) {} + + async create(document: Partial): Promise { + return await this.model.create(document); + } + + async findOne( + filter: FilterQuery, + ): Promise { + return await this.model.findOne(filter); + } + + async findAll( + filter: FilterQuery, + ): Promise { + return await this.model.find(filter); + } + + async findById(id: string): Promise { + return this.model.findById(id).lean(); + } + + async findByClaimId(claimId: string): Promise { + return this.model.find({ claimId: new Types.ObjectId(claimId) }); + } + + async delete(id: string): Promise { + await this.model.findByIdAndDelete(id); + } +} + diff --git a/src/claim-request-management/entites/db-service/claim-sign.db.service.ts b/src/claim-request-management/entites/db-service/claim-sign.db.service.ts new file mode 100644 index 0000000..3522537 --- /dev/null +++ b/src/claim-request-management/entites/db-service/claim-sign.db.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model } from "mongoose"; +import { ClaimSignModel } from "src/claim-request-management/entites/schema/claim-sign"; + +@Injectable() +export class ClaimSignDbService { + constructor( + @InjectModel(ClaimSignModel.name) + private readonly claimSignService: Model, + ) {} + + async create(claimSign: ClaimSignModel): Promise { + return await this.claimSignService.create(claimSign); + } + + async findOne(filter: FilterQuery): Promise { + return await this.claimSignService.findOne(filter); + } +} diff --git a/src/claim-request-management/entites/db-service/damage-image.db.service.ts b/src/claim-request-management/entites/db-service/damage-image.db.service.ts new file mode 100644 index 0000000..4a9b139 --- /dev/null +++ b/src/claim-request-management/entites/db-service/damage-image.db.service.ts @@ -0,0 +1,18 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types } from "mongoose"; +import { DamagePartImageModel } from "src/claim-request-management/entites/schema/damage-image-part.schema"; + +export class DamageImageDbService { + constructor( + @InjectModel(DamagePartImageModel.name) + private readonly model: Model, + ) {} + + async create(image): Promise { + return await this.model.create(image); + } + + async findOne(id: string): Promise { + return await this.model.findById(new Types.ObjectId(id)); + } +} diff --git a/src/claim-request-management/entites/db-service/factor-image.db.service.ts b/src/claim-request-management/entites/db-service/factor-image.db.service.ts new file mode 100644 index 0000000..1d6f52f --- /dev/null +++ b/src/claim-request-management/entites/db-service/factor-image.db.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model } from "mongoose"; +import { ClaimFactorsImage } from "src/claim-request-management/entites/schema/factor-image.schema"; + +@Injectable() +export class ClaimFactorsImageDbService { + constructor( + @InjectModel(ClaimFactorsImage.name) + private readonly model: Model, + ) {} + async create(image): Promise { + return await this.model.create(image); + } + + async findOne( + filter: FilterQuery, + ): Promise { + return await this.model.findOne({ filter }); + } + + async findById(id: string): Promise { + return this.model.findById(id).lean(); + } +} diff --git a/src/claim-request-management/entites/db-service/video-capture.db.service.ts b/src/claim-request-management/entites/db-service/video-capture.db.service.ts new file mode 100644 index 0000000..91b7101 --- /dev/null +++ b/src/claim-request-management/entites/db-service/video-capture.db.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model } from "mongoose"; +import { VideoCaptureModel } from "src/claim-request-management/entites/schema/video-capture.schema"; + +@Injectable() +export class VideoCaptureDbService { + constructor( + @InjectModel(VideoCaptureModel.name) + private readonly videoCapture: Model, + ) {} + async create(video): Promise { + return await this.videoCapture.create(video); + } + + async findOne( + filter: FilterQuery, + ): Promise { + return await this.videoCapture.findOne(filter); + } + + async findById(id: string): Promise { + return this.videoCapture.findById(id).lean(); + } +} diff --git a/src/claim-request-management/entites/schema/action-user.schema.ts b/src/claim-request-management/entites/schema/action-user.schema.ts new file mode 100644 index 0000000..4f3f22b --- /dev/null +++ b/src/claim-request-management/entites/schema/action-user.schema.ts @@ -0,0 +1,13 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import mongoose, { Types } from "mongoose"; + +@Schema({ versionKey: false }) +export class ActionUserModel extends mongoose.Document { + @Prop({ required: true, type: Types.ObjectId }) + userId: Types.ObjectId; + + @Prop({ required: true, type: Date }) + date: Date; +} + +export const ActionActorSchema = SchemaFactory.createForClass(ActionUserModel); diff --git a/src/claim-request-management/entites/schema/ai-image.schema.ts b/src/claim-request-management/entites/schema/ai-image.schema.ts new file mode 100644 index 0000000..cdffd37 --- /dev/null +++ b/src/claim-request-management/entites/schema/ai-image.schema.ts @@ -0,0 +1,7 @@ +import { Prop, Schema } from "@nestjs/mongoose"; + +@Schema({ versionKey: false, _id: true }) +export class AiImagesModel { + @Prop({ type: "array" }) + imagesAddress: string[]; +} diff --git a/src/claim-request-management/entites/schema/car-green-card.schema.ts b/src/claim-request-management/entites/schema/car-green-card.schema.ts new file mode 100644 index 0000000..f0f1b5a --- /dev/null +++ b/src/claim-request-management/entites/schema/car-green-card.schema.ts @@ -0,0 +1,17 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import mongoose, { Types } from "mongoose"; + +@Schema({ versionKey: false, collection: "car-green-cards" }) +export class CarGreenCardModel extends mongoose.Document { + @Prop({ required: false, type: String }) + path?: string; + + @Prop({ required: false, type: String }) + fileName?: string; + + @Prop({ required: false, type: Types.ObjectId }) + claimId?: Types.ObjectId; +} + +export const CarGreenCardSchema = + SchemaFactory.createForClass(CarGreenCardModel); diff --git a/src/claim-request-management/entites/schema/car-parts.schema.ts b/src/claim-request-management/entites/schema/car-parts.schema.ts new file mode 100644 index 0000000..f20d67f --- /dev/null +++ b/src/claim-request-management/entites/schema/car-parts.schema.ts @@ -0,0 +1,18 @@ +import { Prop } from "@nestjs/mongoose"; + +export class CarDamagePartOtherModel { + @Prop({ + format: "array", + description: "please add items of json into array", + example: [{ "حسگر درها": true }], + }) + otherParts?: []; +} + +export class CarDamagePartModel { + @Prop({ type: String }) + side: string; + + @Prop({ type: String }) + part: string; +} diff --git a/src/claim-request-management/entites/schema/claim-base.schema.ts b/src/claim-request-management/entites/schema/claim-base.schema.ts new file mode 100644 index 0000000..aff96de --- /dev/null +++ b/src/claim-request-management/entites/schema/claim-base.schema.ts @@ -0,0 +1,29 @@ +import { Prop } from "@nestjs/mongoose"; +import mongoose, { Types } from "mongoose"; +import { ActionUserModel } from "./action-user.schema"; + +export class ClaimBaseModel extends mongoose.Document { + @Prop() + readonly _id: Types.ObjectId; + + @Prop() + readonly created: Date; + + @Prop({ + required: false, + type: ActionUserModel, + }) + createdBy: ActionUserModel; + + @Prop({ required: false }) + readonly updated: Date; + + @Prop({ + required: false, + type: [ActionUserModel], + }) + updatedBy: ActionUserModel[]; + + @Prop() + readonly deleted: boolean; +} diff --git a/src/claim-request-management/entites/schema/claim-request-management.schema.ts b/src/claim-request-management/entites/schema/claim-request-management.schema.ts new file mode 100644 index 0000000..f9090ce --- /dev/null +++ b/src/claim-request-management/entites/schema/claim-request-management.schema.ts @@ -0,0 +1,330 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { + CarDetail, + RequestManagementModel, +} from "src/request-management/entities/schema/request-management.schema"; +import { UserSignModel } from "src/request-management/entities/schema/sign.schema"; +import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; +import { ClaimStepsEnum } from "src/Types&Enums/claim-request-management/steps.enum"; +import { UserReplyEnum } from "src/Types&Enums/claim-request-management/userReply.enum"; +import { AiImagesModel } from "./ai-image.schema"; +import { CarGreenCardModel } from "./car-green-card.schema"; +import { + CarDamagePartModel, + CarDamagePartOtherModel, +} from "./car-parts.schema"; +import { ImageRequiredModel } from "./image-required.schema"; +import { Plates } from "src/Types&Enums/plate.interface"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { FactorStatus } from "src/Types&Enums/claim-request-management/factor-status.enum"; +import { DaghiOption } from "src/Types&Enums/claim-request-management/daghi-option.enum"; + +// main schema +export type ClaimRequestManagementDoc = ClaimRequestManagementModel & Document; + +export class EffectedUserReply { + @Prop({ required: true, type: String }) + reply?: UserReplyEnum; + + @Prop({ required: true }) + signDetail?: UserSignModel; +} + +export class UserComment { + @Prop({ type: Boolean }) + isAccept: boolean; + + @Prop({ required: false }) + signDetail?: Types.ObjectId; +} + +export class DaghiDetails { + @Prop({ required: true, type: String, enum: DaghiOption }) + option: DaghiOption; + + @Prop({ required: false, type: String }) + price?: string; + + @Prop({ required: false, type: Types.ObjectId }) + branchId?: Types.ObjectId; +} + +export class PartsList { + @Prop({ required: true, type: String }) + partId: string; + + @Prop({ required: true, type: String }) + carPartDamage: string; + + @Prop({ required: true, type: String }) + typeOfDamage: string; + + @Prop({ required: true, type: String }) + price: string; + + @Prop({ required: true, type: String }) + salary: string; + + @Prop({ required: true, type: String }) + totalPayment: string; + + @Prop({ required: true, type: DaghiDetails }) + daghi: DaghiDetails; + + @Prop({ required: true, type: Boolean }) + factorNeeded: boolean; + + @Prop({ type: Types.ObjectId }) + factorLink?: Types.ObjectId; + + @Prop({ type: String, enum: FactorStatus, default: null }) + factorStatus?: FactorStatus; + + @Prop({ type: String, default: null }) + rejectionReason?: string; +} + +export class ActorDetail { + @Prop() + actorName: string; + + @Prop() + actorId: string; +} + +export class InPersonDocuments { + @Prop() + NationalCertificate: string; + + @Prop() + CarCertificate: string; + + @Prop() + DrivingLicense: string; + + @Prop() + CarGreenCard: string; +} + +export class SubmitReply { + @Prop({ required: true }) + description: string; + + @Prop({ required: true }) + actorDetail: ActorDetail; + + @Prop({ required: true, type: [PartsList] }) + parts: PartsList[]; + + @Prop({ required: true, default: () => new Date() }) + submitTime: Date; + + @Prop() + userComment?: UserComment; +} + +export class ActorLockDetails { + @Prop({ type: String }) + fullName?: string; + + @Prop({ type: Types.ObjectId }) + actorId?: Types.ObjectId; +} + +export class ResendCarPartsDto { + @Prop({ required: true }) + partId: string; + + @Prop({ required: true }) + partName: string; +} + +export class ClaimSubmitResend { + @Prop({ required: true }) + resendDescription: string; + + @Prop({ required: true, type: [InPersonDocuments] }) + resendDocuments: InPersonDocuments[]; + + @Prop({ required: true }) + resendCarParts: ResendCarPartsDto[]; +} + +export class UserResendDocuments { + @Prop({ required: false }) + documents: []; + + @Prop({ required: false }) + carParts: []; +} + +export class UserObjection { + @Prop({ type: String, required: true }) + partId: string; + + @Prop({ type: String }) + reason: string; + + @Prop({ type: String }) + partPrice: string; + + @Prop({ type: String }) + partSalary: string; + + @Prop({ type: String }) + typeOfDamage: string; +} + +@Schema({ _id: false }) +export class FileRating { + @Prop({ type: Number, min: 0, max: 5 }) + collisionMethodAccuracy: number; + + @Prop({ type: Number, min: 0, max: 5 }) + evaluationTimeliness: number; + + @Prop({ type: Number, min: 0, max: 5 }) + accidentCauseAccuracy: number; + + @Prop({ type: Number, min: 0, max: 5 }) + guiltyVehicleIdentification: number; +} + +export class PriceDrop { + @Prop({ type: Number }) + total: number; + @Prop({ type: Number }) + carPrice: number; + @Prop({ type: Number }) + carModel: number; + @Prop({ type: [Number] }) + carValue: number[]; + @Prop({ type: Number }) + sumOfSeverity: number; +} + +@Schema({ + collection: "claim-requests-management", + versionKey: false, + timestamps: true, + autoIndex: false, +}) +export class ClaimRequestManagementModel { + readonly aiImage?: any; + constructor() {} + @Prop({ type: RequestManagementModel, unique: true }) + blameFile: RequestManagementModel; + + @Prop({ type: Number, default: 100 }) + requestNumber?: number; + + @Prop({ type: Types.ObjectId }) + userClientKey?: Types.ObjectId; + + @Prop({ type: Types.ObjectId }) + userId: Types.ObjectId; + + @Prop() + fullName: string; + + @Prop() + carDetail: CarDetail; + + @Prop({ type: AddPlateDto }) + carPlate: Plates; + + @Prop({}) + claimStatus: ReqClaimStatus; + + @Prop({}) + steps: ClaimStepsEnum[]; + + @Prop({}) + currentStep: ClaimStepsEnum; + + @Prop({}) + nextStep?: ClaimStepsEnum; + + @Prop({ type: CarDamagePartModel }) + carPartDamage?: CarDamagePartModel[]; + + @Prop({ type: CarDamagePartOtherModel }) + otherParts?: CarDamagePartOtherModel[]; + + @Prop({ format: "string" }) + sheba?: string; + + @Prop({ format: "string" }) + nationalCodeOfInsurer?: string; + + @Prop({ type: CarGreenCardModel, required: false }) + carGreenCard?: CarGreenCardModel; + + @Prop({ type: ImageRequiredModel }) + imageRequired?: ImageRequiredModel; + + @Prop({ type: AiImagesModel }) + aiImages?: AiImagesModel; + + @Prop({ type: EffectedUserReply }) + effectedUserReply?: EffectedUserReply; + + @Prop() + lockFile?: boolean; + + @Prop({ type: Date }) + lockTime?: Date | string | number; + + @Prop({ type: Date }) + unlockTime?: Date | number; + + @Prop() + actorLocked?: ActorLockDetails | null; + + @Prop() + actorsChecker?: []; + + @Prop({ required: false }) + videoCaptureId?: Types.ObjectId; + + @Prop({ required: false }) + damageExpertReply?: SubmitReply | null; + + @Prop({ required: false }) + damageExpertReplyFinal?: SubmitReply | null; + + @Prop({ required: false, type: ClaimSubmitResend }) + damageExpertResend?: ClaimSubmitResend | null; + + @Prop({ type: UserObjection, required: false }) + objection?: UserObjection; + + @Prop({ type: UserResendDocuments }) + userResendDocuments?: UserResendDocuments; + + @Prop({ type: PriceDrop, required: false }) + priceDrop?: PriceDrop; + + @Prop({ type: Map, of: Types.ObjectId, required: false }) + requiredDocuments?: { [key: string]: Types.ObjectId }; + + createdAt: any; + updatedAt: any; + + @Prop({ type: FileRating, required: false }) + rating?: FileRating; + + @Prop({ type: String, required: false }) + visitLocation?: string; + + @Prop({ type: Number, required: false }) + claimNo?: number; + + @Prop({ type: Number, required: false }) + claimId?: number; +} + +export const ClaimRequestManagementSchema = SchemaFactory.createForClass( + ClaimRequestManagementModel, +); diff --git a/src/claim-request-management/entites/schema/claim-required-document.schema.ts b/src/claim-request-management/entites/schema/claim-required-document.schema.ts new file mode 100644 index 0000000..2cbf527 --- /dev/null +++ b/src/claim-request-management/entites/schema/claim-required-document.schema.ts @@ -0,0 +1,27 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum"; + +@Schema({ versionKey: false, collection: "claim-required-documents" }) +export class ClaimRequiredDocument { + @Prop({ required: true, type: String }) + path: string; + + @Prop({ required: true, type: String }) + fileName: string; + + @Prop({ required: true, type: Types.ObjectId }) + claimId: Types.ObjectId; + + @Prop({ required: true, enum: ClaimRequiredDocumentType }) + documentType: ClaimRequiredDocumentType; + + @Prop({ default: () => new Date() }) + uploadedAt: Date; + + _id?: Types.ObjectId; +} + +export const ClaimRequiredDocumentSchema = + SchemaFactory.createForClass(ClaimRequiredDocument); + diff --git a/src/claim-request-management/entites/schema/claim-sign.ts b/src/claim-request-management/entites/schema/claim-sign.ts new file mode 100644 index 0000000..c596e26 --- /dev/null +++ b/src/claim-request-management/entites/schema/claim-sign.ts @@ -0,0 +1,6 @@ +import { Schema, SchemaFactory } from "@nestjs/mongoose"; +import { UserSignModel } from "src/request-management/entities/schema/sign.schema"; + +@Schema({ collection: "claim-sign", versionKey: false }) +export class ClaimSignModel extends UserSignModel {} +export const ClaimSignSchema = SchemaFactory.createForClass(ClaimSignModel); diff --git a/src/claim-request-management/entites/schema/damage-image-part.schema.ts b/src/claim-request-management/entites/schema/damage-image-part.schema.ts new file mode 100644 index 0000000..de0a7da --- /dev/null +++ b/src/claim-request-management/entites/schema/damage-image-part.schema.ts @@ -0,0 +1,18 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +export type DamagePartImage = DamagePartImageModel & Document; + +@Schema({ versionKey: false, collection: "damage-part-image" }) +export class DamagePartImageModel { + @Prop({ default: null }) + path: string | null = null; + + @Prop({ default: null }) + fileName: string | null = null; + + @Prop({ default: null }) + claimId: string = null; +} + +export const DamageImageModelSchema = + SchemaFactory.createForClass(DamagePartImageModel); diff --git a/src/claim-request-management/entites/schema/factor-image.schema.ts b/src/claim-request-management/entites/schema/factor-image.schema.ts new file mode 100644 index 0000000..c791320 --- /dev/null +++ b/src/claim-request-management/entites/schema/factor-image.schema.ts @@ -0,0 +1,28 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; + +@Schema({ versionKey: false, collection: "claim-factors-image" }) +export class ClaimFactorsImage { + @Prop({ required: true, type: String }) + path: string; + + @Prop({ required: true, type: String }) + fileName: string; + + @Prop({ required: true, type: Types.ObjectId }) + claimId: Types.ObjectId; + + @Prop({ required: true, type: String }) + partId: string; + + @Prop({ required: true, type: Object }) + partName: any; + + @Prop({ default: () => new Date() }) + uploadedAt: Date; + + _id?: Types.ObjectId; +} + +export const ClaimFactorsImageSchema = + SchemaFactory.createForClass(ClaimFactorsImage); diff --git a/src/claim-request-management/entites/schema/image-required.schema.ts b/src/claim-request-management/entites/schema/image-required.schema.ts new file mode 100644 index 0000000..4379a95 --- /dev/null +++ b/src/claim-request-management/entites/schema/image-required.schema.ts @@ -0,0 +1,38 @@ +import { Prop, Schema } from "@nestjs/mongoose"; +import { v4 as uuidv4 } from "uuid"; + +@Schema({ versionKey: false, _id: false }) +export class ImageRequiredModel { + @Prop({ type: "array" }) + public aroundTheCar = [ + { side: "left" }, + { side: "back" }, + { side: "front" }, + { side: "right" }, + ]; + + @Prop({ type: "array" }) + public selectPartOfCar = []; + + @Prop({ type: "string" }) + public aiReportText: string; + + constructor(claimFile: any[]) { + this.aroundTheCar.forEach((a) => { + Object.assign(a, { + partId: uuidv4(), + imageId: null, + aiReport: {}, + upload: false, + }); + }); + this.selectPartOfCar = claimFile.map((c, idx) => + Object.assign(c, { + partId: uuidv4(), + aiReport: {}, + imageId: null, + upload: false, + }), + ); + } +} diff --git a/src/claim-request-management/entites/schema/video-capture.schema.ts b/src/claim-request-management/entites/schema/video-capture.schema.ts new file mode 100644 index 0000000..96e6206 --- /dev/null +++ b/src/claim-request-management/entites/schema/video-capture.schema.ts @@ -0,0 +1,17 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import mongoose, { Types } from "mongoose"; + +@Schema({ versionKey: false, collection: "claim-video-capture" }) +export class VideoCaptureModel extends mongoose.Document { + @Prop({ required: false, type: String }) + path?: string; + + @Prop({ required: false, type: String }) + fileName?: string; + + @Prop({ required: false, type: Types.ObjectId }) + claimId?: Types.ObjectId; +} + +export const VideoCaptureSchema = + SchemaFactory.createForClass(VideoCaptureModel); diff --git a/src/claim-request-management/utils/car-part.json b/src/claim-request-management/utils/car-part.json new file mode 100644 index 0000000..e6035eb --- /dev/null +++ b/src/claim-request-management/utils/car-part.json @@ -0,0 +1,181 @@ +{ + "قاب موتور": true, + "گلگیرهاتایرها": true, + "دیسک ترمز": true, + "لنت ترمز": true, + "روغن ترمز": true, + "ترمز دستی": true, + "پیچ چرخ‌ها": true, + "پیستون ترمز": true, + "پدال ترمز": true, + "شلنگ‌های ترمز": true, + "سوزن هواگیر": true, + "سامانه ABS": true, + "پمپ ترمز": true, + "کالیپر": true, + "بوستر": true, + "انواع سوپاپ‌های ترمز": true, + "فنرهای نگهدارنده": true, + "لنگر": true, + "کفشک ترمز": true, + "مخزن روغن ترمز": true, + "واحد تقویت‌کننده هیدرولیکی": true, + "پایه باتری": true, + "کابل باتری": true, + "سینی باتری": true, + "سامانه مدیریت باتری": true, + "دینام": true, + "ترمینال سیم‌کشی باتری": true, + "چراغ‌های جلو و عقب": true, + "چراغ‌های مه‌شکن": true, + "چراغ‌هایی داخل داشبورد": true, + "چراغ سقفی داخل کابین": true, + "حسگر دنده اتوماتیک": true, + "حسگر سرعت": true, + "حسگر دمای مایع خنک‌کننده": true, + "حسگر ترمز ABS": true, + "حسگر اکسیژن": true, + "حسگر جریان هوا": true, + "حسگر کیسه هوا": true, + "حسگر روغن": true, + "حسگر سوخت": true, + "حسگر میل‌لنگ": true, + "حسگر میل بادامک": true, + "حسگر کمربند ایمنی": true, + "حسگر نور": true, + "حسگر درها": true, + "جعبه احتراق": true, + "شمع": true, + "دلکو": true, + "کنترل‌گر زمان": true, + "سیم‌های اتصال": true, + "سوپاپ مغناطیسی": true, + "سیم‌پیچ احتراق": true, + "وایر شمع": true, + "رله فن": true, + "سوئیچ درها": true, + "سوئیچ استارت": true, + "کلید شیشه بالابر": true, + "سوئیچ قفل فرمان": true, + "ترموستات": true, + "تهویه": true, + "موتور": true, + "کف کابین": true, + "ادوات داخل کابین": true, + "اصلی": true, + "دوربین دنده عقب": true, + "دوربین‌های 360 درجه": true, + "صال به زمین": true, + "سامانه قفل مرکزی": true, + "اتصالات برقی درها": true, + "ماژول کنترل ایربگ": true, + "سامانه کنترل سرعت": true, + "سامانه مدیریت موتور": true, + "کروز کنترل": true, + "سامانه ناوبری": true, + "سوکت‌ها": true, + "سیستم قفل از راه دور": true, + "رایانه جعبه‌دنده": true, + "فیوزها": true, + "بلوک سیلندر": true, + "پوشش میل‌لنگ": true, + "پولی میل‌لنگ": true, + "میل‌لنگ": true, + "پولی پمپ آب": true, + "تسمه پروانه": true, + "پیستون": true, + "تسمه دینام": true, + "تسمه تایم": true, + "توربو شارژ": true, + "درپوش سوپاپ": true, + "دسته موتور": true, + "سرسیلندر": true, + "سوپاپ پایت": true, + "سوپاپ تهویه": true, + "شاتون": true, + "پین انگشتی": true, + "بخاری": true, + "میل بادامک": true, + "لرزش‌گیر موتور": true, + "فیلر": true, + "جعبه‌دنده": true, + "پوسته گیربکس": true, + "چرخ‌دنده‌های انتقال قدرت، جناحی، هرزگرد، سرعت‌سنج، چرخ لنگر، فرمان": true, + "پمپ دنده": true, + "دنده": true, + "اهرم تعویض دنده": true, + "دوشاخ دنده": true, + "کوپلینگ دنده": true, + "دیفرانسیل": true, + "سیلندر": true, + "فنر جعبه‌دنده": true, + "محور جعبه‌دنده": true, + "میل‌گاردان": true, + "شفت خروجی": true, + "پولوس‌ها": true, + "سامانه کلاچ": true, + "کابل تعویض دنده": true, + "فیلتر بنزین": true, + "فیلتر هوا": true, + "کاربراتور": true, + "باک سوخت": true, + "سیستم LPG": true, + "کابل ساسات": true, + "منیفولد ورودی": true, + "جداکننده آب از سوخت": true, + "پیل سوختی": true, + "سیستم CNG": true, + "پمپ‌بنزین": true, + "انژکتور": true, + "بدنه دریچه گاز": true, + "خنک‌کننده سوخت": true, + "رگلاتور": true, + "ریل": true, + "غربیلک فرمان": true, + "بازوی فرمان": true, + "جعبه فرمان": true, + "دوک": true, + "شفت فرمان": true, + "سامانه فرمان خودکار": true, + "اتصال جانبی": true, + "اتصال میل موج‌گیر": true, + "بازوی خمیده": true, + "بازوی آزاد": true, + "بازوی پیت – من": true, + "بست و اتصالات": true, + "سیبک فرمان": true, + "کمک‌فنرها": true, + "اتصالات تعادلی": true, + "فنر": true, + "محور خودرو": true, + "عایق حرارتی": true, + "گیره‌ها": true, + "لوله اگزوز": true, + "سپر حرارتی": true, + "صداخفه‌کن": true, + "رزیناتور": true, + "کاتالیست": true, + "حلقه فاصله": true, + "انواع واشرها": true, + "لوله‌های روغن": true, + "کارتل روغن": true, + "پمپ روغن": true, + "واشر پمپ روغن": true, + "صافی روغن": true, + "فیلتر روغن": true, + "تسمه فن": true, + "تیغه فن": true, + "واتر پمپ": true, + "واشر پمپ آب": true, + "دمنده هوا": true, + "بوش فن": true, + "مخزن": true, + "درپوش فشار": true, + "ترموستات": true, + "پوشش فن": true, + "کلاچ فن": true, + "پروانه یا فن": true, + "لوله‌های ورودی و خروجی آب": true, + "زانویی آب": true, + "شلنگ مایع خنک‌کننده": true + } \ No newline at end of file diff --git a/src/client/client.controller.ts b/src/client/client.controller.ts new file mode 100644 index 0000000..b32ea6b --- /dev/null +++ b/src/client/client.controller.ts @@ -0,0 +1,37 @@ +import { + Body, + Controller, + Get, + Post, + UseGuards, +} from "@nestjs/common"; +import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; +import { GlobalGuard } from "src/auth/guards/global.guard"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { ClientService } from "./client.service"; +import { ClientDto } from "./dto/create-client.dto"; + +@Controller("client") +@ApiTags("client-management") +export class ClientController { + constructor(private readonly clientService: ClientService) {} + + @Post() + @UseGuards(GlobalGuard) + @ApiBearerAuth() + async addClient(@Body() client: ClientDto) { + return await this.clientService.addClient(client); + } + + @Get() + @ApiBearerAuth() + @UseGuards(GlobalGuard) + async getClient(@CurrentUser() user) { + return await this.clientService.getClients(); + } + + @Get("list") + async getClientList(@CurrentUser() user) { + return await this.clientService.getClientList(); + } +} diff --git a/src/client/client.module.ts b/src/client/client.module.ts new file mode 100644 index 0000000..b5ec324 --- /dev/null +++ b/src/client/client.module.ts @@ -0,0 +1,24 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { ClientController } from "./client.controller"; +import { ClientService } from "./client.service"; +import { BranchDbService } from "./entities/db-service/branch.db.service"; +import { ClientDbService } from "./entities/db-service/client.db.service"; +import { BranchModel, BranchSchema } from "./entities/schema/branch.schema"; +import { ClientDbSchema, ClientModel } from "./entities/schema/client.schema"; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { name: ClientModel.name, schema: ClientDbSchema }, + { + name: BranchModel.name, + schema: BranchSchema, + }, + ]), + ], + controllers: [ClientController], + providers: [ClientService, ClientDbService, BranchDbService], + exports: [ClientService, ClientDbService, BranchDbService], +}) +export class ClientModule {} diff --git a/src/client/client.service.ts b/src/client/client.service.ts new file mode 100644 index 0000000..fc8db42 --- /dev/null +++ b/src/client/client.service.ts @@ -0,0 +1,56 @@ +import { BadGatewayException, GoneException, Injectable } from "@nestjs/common"; +import { Types } from "mongoose"; +import { + ClientDto, + ClientDtoRs, + ClientLists, +} from "src/client/dto/create-client.dto"; +import { ClientDbService } from "./entities/db-service/client.db.service"; + +@Injectable() +export class ClientService { + constructor(private readonly clientDbService: ClientDbService) {} + async addClient(client: ClientDto): Promise { + try { + const newClient = await this.clientDbService.create({ + clientCode: client.clientCode, + clientName: { + persian: client.clientName.persian, + english: client.clientName.english || null, + }, + property: { + smsApiKey: client.property.smsApiKey || null, + }, + useExpertMode: client.useExpertMode || null, + }); + if (newClient) return new ClientDtoRs(newClient); + else throw new GoneException("database not connected"); + } catch (er) { + throw new BadGatewayException(er.errors); + } + } + + findOne(filter) { + return this.clientDbService.findOne(filter); + } + + async findClientWithPersianName(filter: string) { + return await this.clientDbService.find({ "clientName.persian": filter }); + } + + async findClientWithCompanyCode(companyCode: number) { + return await this.clientDbService.find({ clientCode: companyCode }); + } + + async getClients(): Promise { + const clients = await this.clientDbService.findAll(); + const show = clients.map((c) => new ClientDtoRs(c)); + return show; + } + + async getClientList(): Promise { + const client = await this.clientDbService.findAll(); + const list = client.map((element) => new ClientLists(element)); + return list; + } +} diff --git a/src/client/dto/create-branch.dto.ts b/src/client/dto/create-branch.dto.ts new file mode 100644 index 0000000..fa704ea --- /dev/null +++ b/src/client/dto/create-branch.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsString, IsNotEmpty, IsOptional } from "class-validator"; + +export class CreateBranchDto { + @ApiProperty({ example: "شهرک غرب" }) + @IsString() + @IsNotEmpty() + name: string; + + @ApiProperty({ example: "1234" }) + @IsString() + @IsNotEmpty() + code: string; + + @ApiProperty({ example: "استان" }) + @IsString() + @IsNotEmpty() + city: string; + + @ApiProperty({ example: "شهر" }) + @IsString() + @IsNotEmpty() + state: string; + + @ApiProperty({ example: "فلان آدرس" }) + @IsString() + @IsNotEmpty() + address: string; + + @ApiProperty({ required: false, example: "0912345678" }) + @IsOptional() + @IsString() + phoneNumber?: string; +} diff --git a/src/client/dto/create-client.dto.ts b/src/client/dto/create-client.dto.ts new file mode 100644 index 0000000..9644946 --- /dev/null +++ b/src/client/dto/create-client.dto.ts @@ -0,0 +1,48 @@ +import { Injectable } from "@nestjs/common"; +import { ApiProperty } from "@nestjs/swagger"; +import { Types } from "mongoose"; + +class ClientName { + @ApiProperty({}) + persian: string; + + @ApiProperty({}) + english: string; +} +class Property { + @ApiProperty({}) + smsApiKey: string; +} + +@Injectable() +export class ClientDto { + @ApiProperty({ required: true }) + clientName: ClientName; + + @ApiProperty({ required: true }) + clientCode: number; + + @ApiProperty({ required: false }) + property: Property; + + @ApiProperty({ examples: ["legal", "genuine"] }) + useExpertMode: "legal" | "genuine"; +} +export class ClientDtoRs { + persian: string; + english: string; + clientId: Types.ObjectId; + useExpertsMode: string; + constructor(readonly client) { + this.persian = client.clientName.persian; + } +} + +export class ClientLists { + name: string; + id: Types.ObjectId; + constructor(client: ClientDto) { + this.name = client.clientName.persian; + this.id = client["_id"]; + } +} diff --git a/src/client/entities/db-service/branch.db.service.ts b/src/client/entities/db-service/branch.db.service.ts new file mode 100644 index 0000000..8b2af9f --- /dev/null +++ b/src/client/entities/db-service/branch.db.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, Types } from "mongoose"; +import { BranchModel, BranchDocument } from "../schema/branch.schema"; + +@Injectable() +export class BranchDbService { + constructor( + @InjectModel(BranchModel.name) + private readonly branchModel: Model, + ) {} + + async create(branch: BranchModel): Promise { + return await this.branchModel.create(branch); + } + + async find(branch: FilterQuery): Promise { + return await this.branchModel.findOne(branch); + } + + async findOne(branch: FilterQuery) { + return this.branchModel.findOne(branch); + } + + async findAll(insuranceId: string): Promise { + return await this.branchModel.find({ + clientKey: new Types.ObjectId(insuranceId), + }); + } + + async findById(id: string): Promise { + return this.branchModel.findById(id).lean(); + } +} diff --git a/src/client/entities/db-service/client.db.service.ts b/src/client/entities/db-service/client.db.service.ts new file mode 100644 index 0000000..e218faa --- /dev/null +++ b/src/client/entities/db-service/client.db.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model } from "mongoose"; +import { ClientModel, ClientDocument } from "../schema/client.schema"; + +@Injectable() +export class ClientDbService { + constructor( + @InjectModel(ClientModel.name) + private readonly clientModel: Model, + ) {} + + async create(client: ClientModel): Promise { + return await this.clientModel.create(client); + } + + async find(client: FilterQuery): Promise { + return await this.clientModel.findOne(client); + } + + async findOne(client: FilterQuery) { + return this.clientModel.findOne(client); + } + + async findAll(): Promise { + return await this.clientModel.find(); + } +} diff --git a/src/client/entities/schema/branch.schema.ts b/src/client/entities/schema/branch.schema.ts new file mode 100644 index 0000000..86d9b44 --- /dev/null +++ b/src/client/entities/schema/branch.schema.ts @@ -0,0 +1,32 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; + +export type BranchDocument = BranchModel & Document; + +@Schema({ collection: "branches", versionKey: false, timestamps: true }) +export class BranchModel { + @Prop({ required: true, type: Types.ObjectId, ref: "ClientModel" }) + clientKey: Types.ObjectId; + + @Prop({ required: true }) + name: string; + + @Prop({ required: true }) + code: string; + + @Prop({ required: true }) + city: string; + + @Prop({ required: true }) + state: string; + + @Prop({ required: true }) + address: string; + + @Prop() + phoneNumber?: string; +} + +export const BranchSchema = SchemaFactory.createForClass(BranchModel); + +BranchSchema.index({ clientKey: 1, code: 1 }, { unique: true }); diff --git a/src/client/entities/schema/client.schema.ts b/src/client/entities/schema/client.schema.ts new file mode 100644 index 0000000..185c476 --- /dev/null +++ b/src/client/entities/schema/client.schema.ts @@ -0,0 +1,25 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +export type ClientDocument = ClientModel & Document; + +@Schema({ collection: "clients", versionKey: false }) +export class ClientModel { + @Prop({ required: true, unique: true, type: Object }) + clientName: { + persian: string; + english: string; + }; + + @Prop({ required: false, unique: true, type: Object }) + property: { + smsApiKey: string; + }; + + @Prop({ required: true, unique: false }) + useExpertMode: "legal" | "genuine"; + + @Prop({ required: true, unique: false }) + clientCode: number; +} + +export const ClientDbSchema = SchemaFactory.createForClass(ClientModel); diff --git a/src/decorators/clientKey.decorator.ts b/src/decorators/clientKey.decorator.ts new file mode 100644 index 0000000..408c2dc --- /dev/null +++ b/src/decorators/clientKey.decorator.ts @@ -0,0 +1,9 @@ +import { createParamDecorator, ExecutionContext } from "@nestjs/common"; + +export const ClientKey = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + + return request.user.clientKey; + }, +); diff --git a/src/decorators/customeHeader.decorator.ts b/src/decorators/customeHeader.decorator.ts new file mode 100644 index 0000000..83e1f8f --- /dev/null +++ b/src/decorators/customeHeader.decorator.ts @@ -0,0 +1,8 @@ +import { ExecutionContext, createParamDecorator } from "@nestjs/common"; + +export const CustomHeader = createParamDecorator( + (data, ctx: ExecutionContext) => { + console.log(ctx.switchToHttp().getRequest()); + console.log(ctx.getType()); + }, +); diff --git a/src/decorators/roles.decorator.ts b/src/decorators/roles.decorator.ts new file mode 100644 index 0000000..58a6a77 --- /dev/null +++ b/src/decorators/roles.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from "@nestjs/common"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +export const Roles = (...args: RoleEnum[]) => SetMetadata("role", args); diff --git a/src/decorators/user.decorator.ts b/src/decorators/user.decorator.ts new file mode 100644 index 0000000..47d5656 --- /dev/null +++ b/src/decorators/user.decorator.ts @@ -0,0 +1,8 @@ +import { createParamDecorator, ExecutionContext } from "@nestjs/common"; + +export const CurrentUser = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + return request.user; + }, +); diff --git a/src/expert-blame/dto/all-request.dto.ts b/src/expert-blame/dto/all-request.dto.ts new file mode 100644 index 0000000..b800908 --- /dev/null +++ b/src/expert-blame/dto/all-request.dto.ts @@ -0,0 +1,103 @@ +import { RequestManagementModel } from "src/request-management/entities/schema/request-management.schema"; + +export class AllRequestDto { + // TODO fix interface for class + firstPartyPlate: object; + secondPartyPlate: object; + secondPartyCar: string | undefined; + firstPartyCar: string | undefined; + requestId: string; + submitTime: string; + requestCode: string; + date: Date | string; + time: Date | string; + firstPartyDetail: Object | undefined; + secondPartyDetail: Object | undefined; + requestStatus: string; + lockFile: boolean | undefined; + lockTime: any; + userComment: boolean | null; + status: string; + partiesInitialForms: Object; + type: string; + constructor(request: RequestManagementModel) { + // TODO FIX THIS OBJECT AND CLASS FOR CLEAN CODE + this.requestId = request["_id"]; + this.status = request.blameStatus; + this.userComment = this.userCommentVoid(request); + this.requestCode = request.requestNumber; + + // Format date and time with Iran timezone (Asia/Tehran) + if (request.createdAt) { + const dateFormatOptions: Intl.DateTimeFormatOptions = { + timeZone: "Asia/Tehran", + year: "numeric", + month: "2-digit", + day: "2-digit", + }; + + const timeFormatOptions: Intl.DateTimeFormatOptions = { + timeZone: "Asia/Tehran", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + + this.date = `${new Date(request.createdAt).toLocaleDateString("fa-IR", dateFormatOptions)} `; + this.time = new Date(request.createdAt).toLocaleTimeString("fa-IR", timeFormatOptions); + } else { + this.date = ""; + this.time = ""; + } + + this.lockFile = request.lockFile; + this.lockTime = request.lockTime; + this.type = request.type || "THIRD_PARTY"; // Include type field, default to THIRD_PARTY for backward compatibility + this.partiesInitialForms = { + firstParty: Object.entries( + request.firstPartyDetails.firstPartyInitialForm, + ) + .filter(([key, value]) => value) + .flat()[0], + secondParty: Object.entries( + request.secondPartyDetails.secondPartyInitialForm, + ) + .filter(([key, value]) => (value ? key : null)) + .flat()[0], + }; + this.firstPartyCar = + request.firstPartyDetails?.firstPartyCarDetail?.carName; + this.secondPartyCar = + request.secondPartyDetails?.secondPartyCarDetail?.carName; + } + + userCommentVoid(request: RequestManagementModel): boolean | null { + if ( + request?.expertSubmitReply?.firstPartyComment && + request?.expertSubmitReply?.secondPartyComment + ) { + if ( + request.expertSubmitReply.firstPartyComment.isAccept || + request.expertSubmitReply.secondPartyComment.isAccept + ) { + return true; + } else { + return false; + } + } else { + return null; + } + } + getFirstValidKey(obj: Record) { + return ( + Object.entries(obj).find(([_, value]) => value === true)?.[0] ?? null + ); + } +} + +export class AllRequestDtoRs { + public data; + constructor(requests: RequestManagementModel[]) { + this.data = requests.map((r) => new AllRequestDto(r)); + } +} diff --git a/src/expert-blame/dto/reply.dto.ts b/src/expert-blame/dto/reply.dto.ts new file mode 100644 index 0000000..12f5d64 --- /dev/null +++ b/src/expert-blame/dto/reply.dto.ts @@ -0,0 +1,72 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Types } from "mongoose"; +import { BlameDocumentType } from "src/request-management/entities/schema/blame-document.schema"; + +export class AccidentWayIF { + @ApiProperty({ required: true }) + id: string; + + @ApiProperty({ required: true }) + label: string; +} +export class AccidentReasonIF { + @ApiProperty({ required: true }) + id: string; + + @ApiProperty({ required: true }) + label: string; + + @ApiProperty({ required: true }) + fanavaran: number; +} + +export class FieldsInterface { + @ApiProperty({ required: true }) + accidentWay: AccidentWayIF; + + @ApiProperty({ required: true }) + accidentReason: AccidentReasonIF; + + @ApiProperty({ required: true }) + accidentType: AccidentWayIF; +} + +export class SubmitReplyDto { + @ApiProperty({ required: true }) + description: string; + + @ApiProperty({ required: true, type: Types.ObjectId }) + guiltyUserId: Types.ObjectId; + + @ApiProperty({ required: true }) + fields: FieldsInterface; +} + +export class ResendFirstPartyDto { + voice?: string; + userReply?: string; + @ApiProperty({ required: false }) + firstPartyId: string | null; + + @ApiProperty({ required: false }) + firstPartyDescription: string | null; + + documents?: { [key in BlameDocumentType]?: Types.ObjectId | string }; +} + +export class ResendSecondPartyDto { + voice?: string; + userReply?: string; + @ApiProperty({ required: false }) + secondPartyId: string | null; + + @ApiProperty({ required: false }) + secondPartyDescription: string | null; + + documents?: { [key in BlameDocumentType]?: Types.ObjectId | string }; +} + +export interface SendAginIF { + first: ResendFirstPartyDto; + second: ResendSecondPartyDto; +} diff --git a/src/expert-blame/expert-blame.controller.ts b/src/expert-blame/expert-blame.controller.ts new file mode 100644 index 0000000..b6e5699 --- /dev/null +++ b/src/expert-blame/expert-blame.controller.ts @@ -0,0 +1,156 @@ +import { + Controller, + Get, + Body, + Param, + UseGuards, + Put, + Req, + Res, + Headers, + UseInterceptors, + Patch, +} from "@nestjs/common"; +import { + ApiBearerAuth, + ApiBody, + ApiOkResponse, + ApiParam, + ApiProduces, + ApiTags, +} from "@nestjs/swagger"; +import { Response, Request } from "express"; +import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { ClientKey } from "src/decorators/clientKey.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { LoggingInterceptor } from "src/interceptor/logging.interceptors"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { + ResendFirstPartyDto, + ResendSecondPartyDto, + SendAginIF, + SubmitReplyDto, +} from "./dto/reply.dto"; +import { ExpertBlameService } from "./expert-blame.service"; + +@ApiTags("expert-blame-panel") +@Controller("expert-blame") +@ApiBearerAuth() +@UseGuards(LocalActorAuthGuard, RolesGuard) +@Roles(RoleEnum.EXPERT) +export class ExpertBlameController { + constructor(private readonly expertBlameService: ExpertBlameService) {} + + // TODO role guard for expert fix + @Roles(RoleEnum.EXPERT) + @Get() + async findAll(@CurrentUser() actor, @ClientKey() client) { + return await this.expertBlameService.findAll(actor); + } + + @Roles(RoleEnum.EXPERT) + @Get(":id") + async findOne(@Param("id") id: string, @CurrentUser() actor) { + return await this.expertBlameService.findOne(id, actor.sub); + } + + @Roles(RoleEnum.EXPERT) + @Put("lock/:id") + async lockRequest(@Param("id") id: string, @CurrentUser() actor) { + return await this.expertBlameService.lockRequest(id, actor); + } + + @Roles(RoleEnum.EXPERT) + @Get("request/accident-fields") + async getAccidentFields() { + return await this.expertBlameService.getAccidentField(); + } + + @Roles(RoleEnum.EXPERT) + @Put("reply/submit/:id") + @ApiBody({ type: SubmitReplyDto }) + @ApiParam({ name: "id" }) + async submitReply( + @Param("id") id: string, + @Body() body: SubmitReplyDto, + @CurrentUser() actor, + ) { + return await this.expertBlameService.replyRequest(id, body, actor.sub); + } + + private async handleResendRequest( + id: string, + body: SendAginIF, + actorSub: string, + req: Request, + ) { + return await this.expertBlameService.sendAgainRequest( + id, + body, + actorSub, + req, + ); + } + + @Roles(RoleEnum.EXPERT) + @Put("reply/resend/first/:id") + @ApiBody({ type: ResendFirstPartyDto }) + @ApiParam({ name: "id" }) + async resendFirstParty( + @Param("id") id: string, + @Body() body: SendAginIF, + @CurrentUser() actor, + @Req() req: Request, + ) { + return this.handleResendRequest(id, body, actor.sub, req); + } + @Roles(RoleEnum.EXPERT) + @Put("reply/resend/second/:id") + @Roles(RoleEnum.EXPERT) + @ApiBody({ type: ResendSecondPartyDto }) + @ApiParam({ name: "id" }) + async resendSecondParty( + @Param("id") id: string, + @Body() body: SendAginIF, + @CurrentUser() actor, + @Req() req: Request, + ) { + return this.handleResendRequest(id, body, actor.sub, req); + } + @Roles(RoleEnum.EXPERT) + @Get("stream/:requestId") + @ApiParam({ name: "requestId" }) + async streamVideo(@Param("requestId") requestId: string) { + return this.expertBlameService.streamVideo(requestId); + } + + @UseInterceptors(LoggingInterceptor) + @Get("voice/:requestId/:voiceId") + @Roles(RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT) + @ApiParam({ name: "requestId" }) + @ApiParam({ name: "voiceId" }) + @ApiOkResponse({ + schema: { + type: "string", + format: "binary", + }, + }) + @ApiProduces("mp3") + async downloadVoice( + @Param("voiceId") voiceId, + @Param("requestId") requestId: string, + @Res({ passthrough: true }) res: Response, + @Req() req: Request, + @Headers() headers, + ) { + return await this.expertBlameService.streamVoice(requestId, voiceId); + } + + @ApiParam({ name: "id" }) + @Patch(":id/visit") + async inPersonVisit(@Param("id") requestId: string, @CurrentUser() actor) { + return await this.expertBlameService.inPersonVisit(requestId, actor); + } +} diff --git a/src/expert-blame/expert-blame.module.ts b/src/expert-blame/expert-blame.module.ts new file mode 100644 index 0000000..16c7908 --- /dev/null +++ b/src/expert-blame/expert-blame.module.ts @@ -0,0 +1,15 @@ +import { Module } from "@nestjs/common"; +import { ClientModule } from "src/client/client.module"; +import { PlatesModule } from "src/plates/plates.module"; +import { UsersModule } from "src/users/users.module"; +import { ExpertBlameController } from "./expert-blame.controller"; +import { ExpertBlameService } from "./expert-blame.service"; +import { RequestManagementModule } from "src/request-management/request-management.module"; + +@Module({ + imports: [UsersModule, ClientModule, RequestManagementModule, PlatesModule], + controllers: [ExpertBlameController], + providers: [ExpertBlameService], + exports: [ExpertBlameService], +}) +export class ExpertBlameModule {} diff --git a/src/expert-blame/expert-blame.service.ts b/src/expert-blame/expert-blame.service.ts new file mode 100644 index 0000000..eed9b1a --- /dev/null +++ b/src/expert-blame/expert-blame.service.ts @@ -0,0 +1,730 @@ +import { + BadRequestException, + ForbiddenException, + Injectable, + Logger, + NotFoundException, +} from "@nestjs/common"; +import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; +import { AllRequestDtoRs } from "./dto/all-request.dto"; +import { UserType } from "src/Types&Enums/userType.enum"; +import { SubmitReplyDto } from "./dto/reply.dto"; +import { Types } from "mongoose"; +import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service"; +import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service"; +import { ClientDbService } from "src/client/entities/db-service/client.db.service"; +import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; +import { buildFileLink } from "src/helpers/urlCreator"; +import { readFile } from "fs/promises"; +import { ExpertDbService } from "src/users/entities/db-service/expert.db.service"; +import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service"; +import { UserSignDbService } from "src/request-management/entities/db-service/sign.db.service"; + +interface CheckedRequestEntry { + CheckedRequest?: { + actorId: string; + fullName: string; + }; + [key: string]: any; +} + +@Injectable() +export class ExpertBlameService { + private readonly logger = new Logger(ExpertBlameService.name); + constructor( + private readonly requestManagementDbService: RequestManagementDbService, + private readonly clientDbService: ClientDbService, + private readonly blameVideoDbService: BlameVideoDbService, + private readonly blameVoiceDbService: BlameVoiceDbService, + private readonly expertDbService: ExpertDbService, + private readonly blameDocumentDbService: BlameDocumentDbService, + private readonly userSignDbService: UserSignDbService, + ) {} + + async findAll(actor: any): Promise { + // 1. Fetch all potentially relevant requests from the database. + // Exclude CAR_BODY type requests as they are automatically handled and don't need expert review + const allRequests = await this.requestManagementDbService.findAll({ + "firstPartyDetails.firstPartyPlate": { $ne: null }, + "secondPartyDetails.secondPartyPlate": { $ne: null }, + blameStatus: { + $in: [ + ReqBlameStatus.UnChecked, + ReqBlameStatus.CloseRequest, + ReqBlameStatus.CheckAgain, + ReqBlameStatus.ReviewRequest, + ], + }, + // Both parties must have submitted their initial forms + "firstPartyDetails.firstPartyInitialForm": { $exists: true }, + "secondPartyDetails.secondPartyInitialForm": { $exists: true }, + type: { $ne: "CAR_BODY" }, // Exclude CAR_BODY type requests + }); + + // 2. Filter requests that need expert review based on initial form logic + // Expert is needed when there's a conflict (both claim damaged, both claim guilty, etc.) + // Expert is NOT needed when one says imDamaged and the other says imGuilty (auto-resolved) + const requestsNeedingExpert = []; + for (const request of allRequests) { + const firstPartyForm = request.firstPartyDetails?.firstPartyInitialForm; + const secondPartyForm = request.secondPartyDetails?.secondPartyInitialForm; + + if (!firstPartyForm || !secondPartyForm) { + continue; // Skip if forms are not complete + } + + // Check if this can be auto-resolved (one says damaged, other says guilty) + const canAutoResolve = + (firstPartyForm.imDamaged && secondPartyForm.imGuilty) || + (secondPartyForm.imDamaged && firstPartyForm.imGuilty); + + // If it can be auto-resolved, skip it (no expert needed) + if (canAutoResolve) { + continue; + } + + // Otherwise, expert is needed (both damaged, both guilty, or other conflicts) + requestsNeedingExpert.push(request); + } + + // 3. Filter the requests in memory based on the expert's specific access rights. + const visibleRequests = []; + for (const request of requestsNeedingExpert) { + // For expert-initiated files, only show to the initiating expert + if (request.expertInitiated && request.initiatedBy) { + if (String(request.initiatedBy) !== actor.sub) { + continue; // Skip if not the initiating expert + } + // Expert-initiated files are always visible to the initiating expert + visibleRequests.push(request); + continue; + } + + // For normal files, use existing client-based filtering + const firstPartyClientId = + request.firstPartyDetails?.firstPartyClient?.clientId?.toString(); + const secondPartyClientId = + request.secondPartyDetails?.secondPartyClient?.clientId?.toString(); + + const partyClientIds = [firstPartyClientId, secondPartyClientId] + .filter(Boolean) + .map((id) => new Types.ObjectId(id)); + + if (partyClientIds.length === 0) { + continue; + } + + let clientQuery: any = { _id: { $in: partyClientIds } }; + + if (actor.userType === UserType.LEGAL) { + clientQuery = { + $and: [ + { _id: { $in: partyClientIds } }, + { _id: new Types.ObjectId(actor.clientKey) }, + ], + }; + } + + const client = await this.clientDbService.findOne(clientQuery); + + if (!client) { + continue; + } + + const isExpertTypeMatch = client.useExpertMode === actor.userType; + if (!isExpertTypeMatch) { + continue; + } + + if (request.blameStatus === ReqBlameStatus.CheckAgain) { + if (String(request.actorLocked?.actorId) === actor.sub) { + visibleRequests.push(request); + } + } else { + visibleRequests.push(request); + } + } + + return new AllRequestDtoRs(visibleRequests); + } + + public unlockApi(request, timer) { + return setTimeout(async () => { + try { + const r = await this.requestManagementDbService.findOne(request._id); + + const updateExp: any = { + lockFile: false, + unlockTime: null, + }; + + const shouldDecrementChecked = + r.blameStatus === ReqBlameStatus.ReviewRequest && + !r.expertSubmitReply && + r.actorLocked?.actorId; + + if (shouldDecrementChecked) { + updateExp.blameStatus = ReqBlameStatus.UnChecked; + + await this.expertDbService.findOneAndUpdate( + { _id: new Types.ObjectId(r.actorLocked.actorId) }, + { + $inc: { "requestStats.totalChecked": -1 }, + $pull: { countedRequests: r._id.toString() }, + }, + ); + + this.logger.warn( + `Request ${r._id} unlocked without reply — expert stats rolled back.`, + ); + } + + await this.requestManagementDbService.findByIdAndUpdate( + r._id.toString(), + updateExp, + ); + this.logger.log(`Unlock completed for request: ${r._id}`); + } catch (error) { + this.logger.error(`Failed to unlock request ${request._id}`, error); + } + }, timer); + } + + public scheduleUnlock(request) { + const unlockDelay = new Date(request.unlockTime).getTime() - Date.now(); + if (unlockDelay <= 0) return; // already expired + + setTimeout(async () => { + try { + // Double-check latest state before unlocking + const current = await this.requestManagementDbService.findOne( + request._id, + ); + + if (!current.lockFile || current.expertSubmitReply) { + // Already unlocked or replied + return; + } + + // If expiry passed + if (current.unlockTime && new Date(current.unlockTime) <= new Date()) { + const shouldRollbackStats = + current.blameStatus === ReqBlameStatus.ReviewRequest && + !current.expertSubmitReply && + current.actorLocked?.actorId; + + const update: any = { + lockFile: false, + unlockTime: null, + lockTime: null, + }; + + if (shouldRollbackStats) { + update.blameStatus = ReqBlameStatus.UnChecked; + + await this.expertDbService.findOneAndUpdate( + { _id: new Types.ObjectId(current.actorLocked.actorId) }, + { + $inc: { "requestStats.totalChecked": -1 }, + $pull: { countedRequests: current._id.toString() }, + }, + ); + + this.logger.warn( + `Request ${current._id} auto-unlocked (no reply) — expert stats rolled back.`, + ); + } + + await this.requestManagementDbService.findByIdAndUpdate( + String(current._id), + update, + ); + + this.logger.log(`Auto-unlock completed for request: ${current._id}`); + } + } catch (err) { + this.logger.error(`Auto-unlock failed for ${request._id}`, err); + } + }, unlockDelay); + } + + async findOne(requestId: string, actorId: string) { + // 1. Fetch the main request document + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // 1.5. Reject CAR_BODY type requests as they don't need expert review + if (request.type === "CAR_BODY") { + throw new ForbiddenException( + "CAR_BODY type requests are automatically handled and do not require expert review.", + ); + } + + // 2. Initial validation to ensure the expert has access + if (String(request?.actorLocked?.actorId) === actorId && request.lockFile) { + // This is the correct expert, and the file is locked to them, which is fine. + } else if ( + request.lockFile || + request.blameStatus === ReqBlameStatus.ReviewRequest + ) { + // The file is locked, but not by the current expert. + throw new BadRequestException("Request is locked by another expert"); + } + + // 3. Populate the resend links if the data exists + if (request.expertResendReply) { + const populatePartyLinks = async ( + partyKey: "firstParty" | "secondParty", + ) => { + const partyReply = request.expertResendReply[partyKey]; + if (!partyReply) return; + + // Populate the voice link + if (partyReply.voice) { + const voiceDoc = await this.userSignDbService.findById( + partyReply.voice.toString(), + ); + if (voiceDoc) { + partyReply.voice = buildFileLink(voiceDoc.path); + } + } + + // Populate the document links + if (partyReply.documents) { + for (const docType in partyReply.documents) { + const docId = partyReply.documents[docType]; + if (docId) { + const doc = await this.blameDocumentDbService.findById( + docId.toString(), + ); + if (doc) { + partyReply.documents[docType] = buildFileLink(doc.path); // Replace ID with URL + } + } + } + } + }; + + await populatePartyLinks("firstParty"); + await populatePartyLinks("secondParty"); + } + + // 4. Populate the Signature Links from the correct reply object + // First, determine which reply object is the final, authoritative one. + const finalReply = + request.expertSubmitReplyFinal || request.expertSubmitReply; + + if (finalReply) { + const populateSignatureLink = async ( + commentField: "firstPartyComment" | "secondPartyComment", + ) => { + const comment = finalReply[commentField]; + // Check if the comment and its signDetail with a fileId exist + if (comment?.signDetail?.fileId) { + const signDoc = await this.userSignDbService.findById( + comment.signDetail.fileId.toString(), + ); + if (signDoc) { + // Add a new 'fileUrl' property to the signDetail object + (comment.signDetail as any).fileUrl = buildFileLink(signDoc.path); + } + } + }; + // Run the population for both parties' signatures on the correct reply object. + await populateSignatureLink("firstPartyComment"); + await populateSignatureLink("secondPartyComment"); + } + + // 5. Format the date for display with Iran timezone (Asia/Tehran) + if (request.createdAt) { + const formattingOptions: Intl.DateTimeFormatOptions = { + timeZone: "Asia/Tehran", + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + request.createdAt = new Date(request.createdAt).toLocaleString( + "fa-IR", + formattingOptions, + ); + } + + // 6. Return the fully populated request object + return request; + } + + async lockRequest(requestId: string, actorDetail) { + const fifteenMinutes = new Date(Date.now() + 15 * 60 * 1000); + + const updateResult = await this.requestManagementDbService.findOneAndUpdate( + { + _id: requestId, + lockFile: false, + blameStatus: { $ne: ReqBlameStatus.UserPending }, + }, + { + $set: { + lockFile: true, + blameStatus: ReqBlameStatus.ReviewRequest, + unlockTime: fifteenMinutes, + lockTime: new Date(), + actorLocked: { + fullName: actorDetail.fullName, + actorId: new Types.ObjectId(actorDetail.sub), + }, + }, + $push: { + actorsChecker: { + [ReqBlameStatus.ReviewRequest]: { + fullName: actorDetail.fullName, + actorId: new Types.ObjectId(actorDetail.sub), + }, + Date: new Date(), + }, + }, + }, + { new: true }, + ); + + if (!updateResult) { + throw new BadRequestException("Request already locked or invalid status"); + } + + // Update expert stats atomically (use findOneAndUpdate with conditions) + await this.updateDamageExpertStats(actorDetail.sub, requestId, "checked"); + + this.scheduleUnlock(updateResult); + return { _id: requestId, lock: true }; + } + + private async updateDamageExpertStats( + expertId: string, + requestId: string, + type: "checked" | "handled", + ) { + if (!expertId || !requestId || !["checked", "handled"].includes(type)) { + console.warn("Invalid expertId, requestId, or type"); + return; + } + + const expert = await this.expertDbService.findOne({ + _id: new Types.ObjectId(expertId), + }); + + if (!expert) { + console.warn("Expert not found:", expertId); + return; + } + + const requestIdStr = new Types.ObjectId(requestId).toString(); + const countedRequestIds = + expert.countedRequests?.map((id) => id.toString()) || []; + + if (type === "checked" && countedRequestIds.includes(requestIdStr)) { + console.log( + `Request ${requestIdStr} already checked for expert ${expertId}`, + ); + return; + } + + const update: any = { $inc: {}, $push: {} }; + + if (type === "checked") { + update.$inc["requestStats.totalChecked"] = 1; + update.$push["countedRequests"] = requestIdStr; + } else if (type === "handled") { + update.$inc["requestStats.totalHandled"] = 1; + + if (countedRequestIds.includes(requestIdStr)) { + update.$inc["requestStats.totalChecked"] = -1; + } + + if (!countedRequestIds.includes(requestIdStr)) { + update.$push["countedRequests"] = requestIdStr; + } else { + delete update.$push; + } + } + + const updateResult = await this.expertDbService.findOneAndUpdate( + { _id: new Types.ObjectId(expertId) }, + update, + ); + + if (!updateResult) { + console.warn("Failed to update expert stats for:", expertId); + } else { + console.log(`Expert stats updated (${type}) for expert:`, expertId); + } + } + + async replyRequest(requestId: string, reply: SubmitReplyDto, userId: string) { + const request = await this.requestManagementDbService.findOne(requestId); + + if (!request) { + throw new NotFoundException("Request not found"); + } + if (String(request.actorLocked?.actorId) !== userId) { + throw new ForbiddenException( + "Access denied to this request. You are not the locked expert.", + ); + } + if (request.unlockTime == null) { + throw new ForbiddenException("Your lock time has expired."); + } + if (!request.lockFile) { + throw new ForbiddenException( + "You must lock the request before submitting a reply.", + ); + } + + const isObjection = !!request.expertResendReply; + const replyField = isObjection + ? "expertSubmitReplyFinal" + : "expertSubmitReply"; + + if (!isObjection && request.expertSubmitReply) { + throw new ForbiddenException( + "This request already has an initial expert reply.", + ); + } + if (isObjection && request.expertSubmitReplyFinal) { + throw new ForbiddenException( + "This request already has a final expert reply.", + ); + } + + const newReplyObject = { + description: reply.description, + submitTime: new Date(), + guiltyUserId: reply.guiltyUserId, + fields: { + accidentWay: { + id: reply.fields.accidentWay.id, + label: reply.fields.accidentWay.label, + }, + accidentReason: { + id: reply.fields.accidentReason.id, + label: reply.fields.accidentReason.label, + fanavaran: reply.fields.accidentReason.fanavaran, + }, + accidentType: { + id: reply.fields.accidentType.id, + label: reply.fields.accidentType.label, + }, + }, + firstPartyComment: request.expertSubmitReply?.firstPartyComment || null, + secondPartyComment: request.expertSubmitReply?.secondPartyComment || null, + }; + + const updatePayload: any = { + $set: { + lockFile: false, + blameStatus: ReqBlameStatus.CheckedRequest, + [replyField]: newReplyObject, + }, + $push: { + actorsChecker: { + [ReqBlameStatus.CheckedRequest]: request.actorLocked, + Date: new Date(), + }, + }, + }; + + if (isObjection) { + updatePayload.$set.expertSubmitReply = newReplyObject; + } + + try { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + return { + requestId: request._id, + blameStatus: ReqBlameStatus.CheckedRequest, + }; + } catch (error) { + this.logger.error("Failed to submit expert reply:", error); + throw new Error("Failed to submit expert reply"); + } + } + + async sendAgainRequest( + requestId: string, + resend: any, + userId: string, + req: any, + ) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + if (String(request.actorLocked?.actorId) !== userId) { + throw new ForbiddenException("Access denied to this request"); + } + if (request.expertSubmitReply) { + throw new ForbiddenException("Request already has an expert reply"); + } + if (request.unlockTime == null) { + throw new ForbiddenException("Your lock time has expired or was not set"); + } + + const partyType = req.route.path.split("/")[4]; + + switch (partyType) { + case "first": { + if (request.expertResendReply?.firstParty) { + throw new ForbiddenException( + "Request has an expert resend reply for the first party", + ); + } + + const { firstPartyId, firstPartyDescription } = resend; + + try { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + lockFile: false, + blameStatus: ReqBlameStatus.UserPending, + "expertResendReply.firstParty.firstPartyId": firstPartyId, + "expertResendReply.firstParty.firstPartyDescription": + firstPartyDescription, + $push: { + actorsChecker: { + [ReqBlameStatus.UserPending]: request.actorLocked, + Date: new Date(), + }, + }, + }, + ); + } catch (error) { + this.logger.error("Failed to update for first party:", error); + throw error; + } + + return { + requestId: request._id, + blameStatus: ReqBlameStatus.UserPending, + }; + } + + case "second": { + if (request.expertResendReply?.secondParty) { + throw new ForbiddenException( + "Request has an expert resend reply for the second party", + ); + } + + const { secondPartyId, secondPartyDescription } = resend; + + try { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + lockFile: false, + blameStatus: ReqBlameStatus.UserPending, + "expertResendReply.secondParty.secondPartyId": secondPartyId, + "expertResendReply.secondParty.secondPartyDescription": + secondPartyDescription, + $push: { + actorsChecker: { + [ReqBlameStatus.UserPending]: request.actorLocked, + Date: new Date(), + }, + }, + }, + ); + } catch (error) { + this.logger.error("Failed to update for second party:", error); + throw error; + } + + // TODO notification for user parties + // TODO send SMS notification + // TODO send URI For USER + return { + requestId: request._id, + blameStatus: ReqBlameStatus.UserPending, + }; + } + + default: + throw new BadRequestException( + `Invalid party type in URL: ${partyType}`, + ); + } + } + + /// VIDEO SERVICE && VOICE SERVICE + // TODO add video service to Object Storage + async streamVideo(requestId): Promise { + const request = await this.requestManagementDbService.findOne(requestId); + const video_path = await this.blameVideoDbService.findOne( + String(request.firstPartyDetails.firstPartyFile.firstPartyVideoId), + ); + return buildFileLink(video_path.path); + } + + async streamVoice(requestId, voiceId) { + try { + const voice = await this.blameVoiceDbService.findOne(voiceId); + if (!voice) throw new NotFoundException("not found voice"); + if (String(voice.requestId) === requestId) { + return buildFileLink(voice.path); + } else { + throw new ForbiddenException( + "Can Not Access To This Voice Because Voice is Not Assign to RequestID", + ); + } + } catch (er) { + if (er) throw new NotFoundException("voice not found ", er); + } + } + + async getAccidentField() { + try { + const ac_reason = await readFile( + "src/static/ACCIDENT_REASON.json", + "utf-8", + ); + const ac_type = await readFile("src/static/ACCIDENT_TYPE.json", "utf-8"); + const ac_way = await readFile("src/static/ACCIDENT_WAY.json", "utf-8"); + return { + accidentReason: JSON.parse(ac_reason), + accidentType: JSON.parse(ac_type), + accidentWay: JSON.parse(ac_way), + }; + } catch (err) { + this.logger.error(err); + } + } + + async inPersonVisit(requestId: string, actorDetail: any) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Blame not found"); + } + + const updated = await this.requestManagementDbService.findAndUpdate( + { _id: new Types.ObjectId(requestId) }, + { + blameStatus: ReqBlameStatus.InPersonVisit, + }, + ); + + await this.expertDbService.updateStats( + actorDetail.sub, + "handled", + requestId, + ); + + return updated; + } +} diff --git a/src/expert-claim/dto/claim-list-perId-rs.dto.ts b/src/expert-claim/dto/claim-list-perId-rs.dto.ts new file mode 100644 index 0000000..f52b4fb --- /dev/null +++ b/src/expert-claim/dto/claim-list-perId-rs.dto.ts @@ -0,0 +1,80 @@ +import { UserObjectionDto } from "src/claim-request-management/dto/user-objection.dto"; +import { + ClaimRequestManagementModel, + SubmitReply, + UserComment, + UserResendDocuments, + PriceDrop, +} from "src/claim-request-management/entites/schema/claim-request-management.schema"; + +export class ClaimPerIdRs { + requestDetail: any; + bankDamageUserInformation: {}; + accidentLocation: {}; + blameFile: any; + aiImages: any; + carPartDamage: any; + imageRequired: any; + carGreenCard: any; + nationalCodeOfInsurer: string; + otherParts: any; + sheba: any; + expertSubmitReply: any; + damageExpertResend: any; + damageExpertReply: SubmitReply; + damageExpertReplyFinal: SubmitReply; + lockFile: boolean; + videoCaptureId: any; + signDetails: UserComment; + lockTime: string | number | Date; + unlockTime: number | Date; + userResendDocuments: UserResendDocuments; + objection: UserObjectionDto; + dropPrice: PriceDrop; + type: string; + requiredDocuments: any; + constructor(request: ClaimRequestManagementModel) { + request.imageRequired.aroundTheCar = request.imageRequired.aroundTheCar.map( + (v) => { + //@ts-ignore + delete v.aiReport; + return v; + }, + ); + request.imageRequired.aroundTheCar = + request.imageRequired.selectPartOfCar.map((v) => { + //@ts-ignore + delete v.aiReport; + return v; + }); + this.requestDetail = { + createdDate: request.createdAt.toLocaleDateString("fa-IR"), + createdTime: request.createdAt.toLocaleTimeString("fa-IR"), + status: request.claimStatus, + }; + delete request.blameFile.actorsChecker; + this.bankDamageUserInformation = { + fullName: request.fullName, + numberOfSheba: request.sheba, + }; + this.blameFile = request.blameFile; + this.videoCaptureId = request?.videoCaptureId; + this.damageExpertResend = request.damageExpertResend; + this.damageExpertReply = request.damageExpertReply; + this.damageExpertReplyFinal = request.damageExpertReplyFinal; + this.lockFile = request.lockFile; + this.lockTime = request.lockTime; + this.unlockTime = request.unlockTime; + this.userResendDocuments = request.userResendDocuments; + this.objection = request.objection as UserObjectionDto; + this.carPartDamage = request.carPartDamage; + this.imageRequired = request.imageRequired; + this.carGreenCard = request.carGreenCard; + this.nationalCodeOfInsurer = request.nationalCodeOfInsurer; + this.otherParts = request.otherParts; + this.sheba = request.sheba; + this.dropPrice = request.priceDrop; + this.type = request.blameFile?.type || "THIRD_PARTY"; // Include type field from blameFile + this.requiredDocuments = request.requiredDocuments; + } +} diff --git a/src/expert-claim/dto/claim-list-rs.dto.ts b/src/expert-claim/dto/claim-list-rs.dto.ts new file mode 100644 index 0000000..ef0fbe3 --- /dev/null +++ b/src/expert-claim/dto/claim-list-rs.dto.ts @@ -0,0 +1,22 @@ +export class ClaimListDtoRs { + public data: any[]; + constructor(claimFile) { + this.data = claimFile.map((i) => { + return { + requestId: i._id, + lockFile: i.isLocked, + claimStatus: i.claimStatus, + requestCode: i.blameFile?.requestNumber, + firstPartyCar: + i.blameFile.firstPartyDetails.firstPartyCarDetail.carName, + secondPartyCar: + i.blameFile.secondPartyDetails?.secondPartyCarDetail.carName, + date: new Date(i.createdAt).toLocaleDateString("fa-IR"), + time: new Date(i.createdAt).toLocaleTimeString("fa-IR"), + status: i.status, + effectedUserReply: i.effectedUserReply?.reply, + type: i.blameFile?.type || "THIRD_PARTY", // Include type field from blameFile + }; + }); + } +} diff --git a/src/expert-claim/dto/factor-validation.dto.ts b/src/expert-claim/dto/factor-validation.dto.ts new file mode 100644 index 0000000..bee838a --- /dev/null +++ b/src/expert-claim/dto/factor-validation.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { + IsArray, + IsEnum, + IsOptional, + IsString, + ValidateNested, +} from "class-validator"; +import { FactorStatus } from "src/Types&Enums/claim-request-management/factor-status.enum"; + +class FactorDecisionDto { + @ApiProperty() + @IsString() + partId: string; + + @ApiProperty({ enum: [FactorStatus.APPROVED, FactorStatus.REJECTED] }) + @IsEnum([FactorStatus.APPROVED, FactorStatus.REJECTED]) + status: FactorStatus; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + rejectionReason?: string; +} + +export class FactorValidationDto { + @ApiProperty({ type: [FactorDecisionDto] }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => FactorDecisionDto) + decisions: FactorDecisionDto[]; +} diff --git a/src/expert-claim/dto/reply.dto.ts b/src/expert-claim/dto/reply.dto.ts new file mode 100644 index 0000000..79c0b73 --- /dev/null +++ b/src/expert-claim/dto/reply.dto.ts @@ -0,0 +1,97 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { TypeOfDamage } from "src/Types&Enums/claim-request-management/type-of-damage.enum"; +import { DaghiOption } from "src/Types&Enums/claim-request-management/daghi-option.enum"; + +export class CarPartDamageDto { + @ApiProperty({ type: String }) + side: string; + + @ApiProperty({ type: String }) + part: string; +} + +export class DaghiDetailsDto { + @ApiProperty({ + required: true, + enum: DaghiOption, + description: "Daghi option: ارزش لوازم بازیافتی, تحویل داغی, فاقد ارزش, با احتساب داغی", + }) + option: DaghiOption; + + @ApiProperty({ + required: false, + type: String, + description: "Price field required when option is 'ارزش لوازم بازیافتی'", + }) + price?: string; + + @ApiProperty({ + required: false, + type: String, + description: "Branch ID required when option is 'تحویل داغی'", + }) + branchId?: string; +} + +export class PartsList { + @ApiProperty({ required: true, type: String }) + partId: string; + + @ApiProperty({ required: true, type: CarPartDamageDto }) + carPartDamage: CarPartDamageDto; + + @ApiProperty({ required: true, type: String }) + typeOfDamage: TypeOfDamage; + + @ApiProperty({ required: true, type: String }) + price: string; + + @ApiProperty({ required: true, type: String }) + salary: string; + + @ApiProperty({ required: true, type: String }) + totalPayment: string; + + @ApiProperty({ required: true, type: DaghiDetailsDto }) + daghi: DaghiDetailsDto; + + @ApiProperty({ required: true, type: Boolean }) + factorNeeded: boolean; +} + +export class ClaimSubmitReplyDto { + @ApiProperty({ required: true }) + description: string; + + @ApiProperty({ required: true, type: [PartsList] }) + parts: PartsList[]; +} + +export class ResendCarPartsDto { + @ApiProperty({ required: true }) + partId: string; + + @ApiProperty({ required: true }) + partName: string; + + @ApiProperty({ required: false }) + side?: string; +} + +class InPersonDocuments { + NationalCertificate: string; + CarCertificate: string; + DrivingLicense: string; + CarGreenCard: string; +} + +export class ClaimSubmitResendDto { + @ApiProperty({ required: true }) + resendDescription: string; + + @ApiProperty({ required: true, examples: InPersonDocuments }) + resendDocuments: InPersonDocuments[]; + + @ApiProperty({ required: true, type: [ResendCarPartsDto] }) + resendCarParts: ResendCarPartsDto[]; +} diff --git a/src/expert-claim/expert-claim.controller.ts b/src/expert-claim/expert-claim.controller.ts new file mode 100644 index 0000000..2b64117 --- /dev/null +++ b/src/expert-claim/expert-claim.controller.ts @@ -0,0 +1,179 @@ +import { + Body, + Controller, + Get, + Header, + Param, + Put, + Query, + Res, + UseGuards, + Headers, + Patch, +} from "@nestjs/common"; +import { + ApiBearerAuth, + ApiBody, + ApiParam, + ApiQuery, + ApiTags, +} from "@nestjs/swagger"; +import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { ClientKey } from "src/decorators/clientKey.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { + ClaimSubmitReplyDto, + ClaimSubmitResendDto, +} from "src/expert-claim/dto/reply.dto"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { ParseJsonPipe } from "src/utils/pipes/parse-json.pipe"; +import { ExpertClaimService } from "./expert-claim.service"; +import { FactorValidationDto } from "./dto/factor-validation.dto"; + +@ApiTags("expert-claim-panel") +@Controller("expert-claim") +@ApiBearerAuth() +@UseGuards(LocalActorAuthGuard, RolesGuard) +@Roles(RoleEnum.DAMAGE_EXPERT) +export class ExpertClaimController { + constructor(private readonly expertClaimService: ExpertClaimService) {} + + @Get() + async getAllClaimRequests(@CurrentUser() actor, @ClientKey() client) { + return await this.expertClaimService.getClaimRequestsListForExpert(actor); + } + + @Get("/:id") + @ApiParam({ name: "id" }) + @ApiQuery({ + name: "updateDropPrice", + required: false, + description: "A stringified JSON object with price drop override values.", + }) + async getClaimRequestPerId( + @Param("id") id: string, + @CurrentUser() currentUser, + @Query("updateDropPrice", new ParseJsonPipe()) priceDrop?: any, + ) { + return await this.expertClaimService.requestPerId( + id, + currentUser, + priceDrop, + ); + } + + @Get("imageRequired/:id") + @ApiParam({ name: "id" }) + async getImageRequired(@Param("id") id: string, @CurrentUser() currentUser) { + return await this.expertClaimService.imageRequired(id, currentUser); + } + + @Get("required-documents/:id") + @ApiParam({ name: "id" }) + async getRequiredDocuments( + @Param("id") id: string, + @CurrentUser() currentUser, + ) { + return await this.expertClaimService.getRequiredDocuments(id, currentUser); + } + + @Put("lock/:id") + async lockClaimRequest(@Param("id") requestId: string, @CurrentUser() actor) { + return await this.expertClaimService.lockClaimRequest(requestId, actor); + } + + @Put("reply/submit/:id") + @ApiParam({ name: "id" }) + async submitReplyRequest( + @Param("id") requestId, + @Body() body: ClaimSubmitReplyDto, + @CurrentUser() actor, + ) { + return await this.expertClaimService.submitReplyRequest( + requestId, + body, + actor, + ); + } + + @Put("resend/submit/:id") + @ApiParam({ name: "id" }) + async submitResend( + @Param("id") requestId, + @Body() body: ClaimSubmitResendDto, + @CurrentUser() actor, + ) { + return await this.expertClaimService.submitResend( + requestId, + body, + actor.sub, + ); + } + + @ApiQuery({ + name: "query", + enum: ["car-capture", "accident"], + required: true, + }) + @Get("stream/:id/video") + @Header("Accept-Ranges", "bytes") + @Header("Content-Type", "video/mp4") + async claimStream( + @Headers() headers, + @Param("id") id: string, + @Query("query") query: "car-capture" | "accident", + @Res() res, + ) { + return this.expertClaimService.streamService(id, query, res, headers); + } + + @ApiQuery({ + name: "query", + enum: ["car-capture", "accident"], + required: true, + }) + @Get("link/:id/video") + @ApiParam({ name: "id" }) + async getClaimVideoLink( + @Param("id") id: string, + @Query("query") query: "car-capture" | "accident", + ) { + return this.expertClaimService.getVideoLink(id, query); + } + + @Get("stream/:id/voice") + @ApiParam({ name: "id" }) + async getClaimVoiceLink(@Param("id") id: string) { + return await this.expertClaimService.getVoiceLink(id); + } + + @Patch("validate-factors/:claimId") + @ApiParam({ name: "claimId" }) + @ApiBody({ type: FactorValidationDto }) + @Roles(RoleEnum.DAMAGE_EXPERT) + async validateFactors( + @Param("claimId") claimId: string, + @Body() validationData: FactorValidationDto, + @CurrentUser() user, + ) { + return this.expertClaimService.validateClaimFactors( + claimId, + validationData.decisions, + user.sub, + ); + } + + @ApiParam({ name: "id" }) + @Patch(":id/visit") + async inPersonVisit(@Param("id") requestId: string, @CurrentUser() actor) { + return await this.expertClaimService.inPersonVisit(requestId, actor); + } + + @Get("branches/:insuranceId") + @ApiParam({ name: "insuranceId" }) + async getInsuranceBranches(@Param("insuranceId") insuranceId: string) { + return await this.expertClaimService.retrieveInsuranceBranches(insuranceId); + } +} diff --git a/src/expert-claim/expert-claim.module.ts b/src/expert-claim/expert-claim.module.ts new file mode 100644 index 0000000..e24ed6c --- /dev/null +++ b/src/expert-claim/expert-claim.module.ts @@ -0,0 +1,40 @@ +import { HttpModule } from "@nestjs/axios"; +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { AiModule } from "src/ai/ai.module"; +import { AuthModule } from "src/auth/auth.module"; +import { ClaimRequestManagementModule } from "src/claim-request-management/claim-request-management.module"; +import { ClaimFactorsImageDbService } from "src/claim-request-management/entites/db-service/factor-image.db.service"; +import { + ClaimFactorsImage, + ClaimFactorsImageSchema, +} from "src/claim-request-management/entites/schema/factor-image.schema"; +import { ClientModule } from "src/client/client.module"; +import { ExpertBlameModule } from "src/expert-blame/expert-blame.module"; +import { RequestManagementModule } from "src/request-management/request-management.module"; +import { SandHubModule } from "src/sand-hub/sand-hub.module"; +import { UsersModule } from "src/users/users.module"; +import { ExpertClaimController } from "./expert-claim.controller"; +import { ExpertClaimService } from "./expert-claim.service"; + +@Module({ + imports: [ + HttpModule, + SandHubModule, + MongooseModule.forFeature([ + { name: ClaimFactorsImage.name, schema: ClaimFactorsImageSchema }, + ]), + AuthModule, + ExpertBlameModule, + ClaimRequestManagementModule, + ExpertBlameModule, + AiModule, + RequestManagementModule, + UsersModule, + ClientModule, + ], + controllers: [ExpertClaimController], + providers: [ExpertClaimService, ClaimFactorsImageDbService], + exports: [ClaimFactorsImageDbService, ExpertClaimService], +}) +export class ExpertClaimModule {} diff --git a/src/expert-claim/expert-claim.service.ts b/src/expert-claim/expert-claim.service.ts new file mode 100644 index 0000000..956f35a --- /dev/null +++ b/src/expert-claim/expert-claim.service.ts @@ -0,0 +1,1481 @@ +"use strict"; +import { createReadStream, existsSync, statSync } from "node:fs"; +import { join } from "node:path"; +import { HttpService } from "@nestjs/axios"; +import { + BadRequestException, + ForbiddenException, + HttpException, + HttpStatus, + Injectable, + Logger, + NotFoundException, +} from "@nestjs/common"; +import { distance as stringDistance } from "fastest-levenshtein"; // حتما نصب بشه +import { Types } from "mongoose"; +import { lastValueFrom } from "rxjs"; +import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service"; +import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service"; +import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service"; +import { ClientDbService } from "src/client/entities/db-service/client.db.service"; +import { buildFileLink } from "src/helpers/urlCreator"; +import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service"; +import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service"; +import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service"; +import { SandHubService } from "src/sand-hub/sand-hub.service"; +import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; +import { ClaimStepsEnum } from "src/Types&Enums/claim-request-management/steps.enum"; +import { UserType } from "src/Types&Enums/userType.enum"; +import { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service"; +import { ClaimPerIdRs } from "./dto/claim-list-perId-rs.dto"; +import { ClaimListDtoRs } from "./dto/claim-list-rs.dto"; +import { ClaimSubmitReplyDto, ClaimSubmitResendDto } from "./dto/reply.dto"; +import { ClaimFactorsImageDbService } from "src/claim-request-management/entites/db-service/factor-image.db.service"; +import { ClaimRequiredDocumentDbService } from "src/claim-request-management/entites/db-service/claim-required-document.db.service"; +import { FactorStatus } from "src/Types&Enums/claim-request-management/factor-status.enum"; +import { DaghiOption } from "src/Types&Enums/claim-request-management/daghi-option.enum"; +import { BranchDbService } from "src/client/entities/db-service/branch.db.service"; + +@Injectable() +export class ExpertClaimService { + private readonly logger = new Logger(ExpertClaimService.name); + + private readonly priceDropPart = { + backFender: { + Minor: 2, + Moderate: 3, + Severe: 5, + }, + backDoor: { + Minor: 1, + Moderate: 2, + Severe: 3, + }, + frontDoor: { + Minor: 1, + Moderate: 2, + Severe: 3, + }, + frontFender: { + Minor: 1, + Moderate: 2, + Severe: 3, + }, + frontBumper: { + Minor: 1, + Moderate: 2, + Severe: 3, + }, + Hood: { + Minor: 2, + moderate: 3, + Severe: 4, + }, + Trunk: { + minor: 1, + moderate: 3, + Severe: 5, + }, + Roof: { + Minor: 3, + Moderate: 5, + Severe: 7, + }, + coil: { + Minor: 2, + Moderate: 3, + Severe: 4, + }, + column: { + Minor: 2, + Moderate: 3, + Severe: 4, + }, + frontTray: { + Minor: 1, + Moderate: 2, + Severe: 3, + }, + backTray: { + Minor: 2, + moderate: 4, + Severe: 5, + }, + frontChassis: { + Minor: 3, + Moderate: 5, + Severe: 7, + }, + backChassis: { + Minor: 2, + Moderate: 4, + Severe: 6, + }, + carFootrest: { + Minor: 1, + Moderate: 2, + Severe: 3, + }, + carFloor: { + Minor: 4, + moderate: 6, + Severe: 8, + }, + }; + + private readonly yearCoefficient = { + 1393: 2.05, + 1394: 2.1, + 1395: 2.2, + 1396: 2.3, + 1397: 2.4, + 1398: 2.5, + 1399: 2.6, + 1400: 2.7, + 1401: 2.8, + 1402: 2.9, + 1403: 3, + 1404: 3, + }; + + constructor( + private readonly blameVoiceDbService: BlameVoiceDbService, + private readonly blameDocumentDbService: BlameDocumentDbService, + private readonly claimRequestManagementDbService: ClaimRequestManagementDbService, + private readonly damageImageDbService: DamageImageDbService, + private readonly blameVideoDbService: BlameVideoDbService, + private readonly claimVideoCaptureDbService: VideoCaptureDbService, + private readonly httpService: HttpService, + private readonly sandHubService: SandHubService, + private readonly damageExpertDbService: DamageExpertDbService, + private readonly clientDbService: ClientDbService, + private readonly claimFactorsImageDbService: ClaimFactorsImageDbService, + private readonly claimRequiredDocumentDbService: ClaimRequiredDocumentDbService, + private readonly branchDbService: BranchDbService, + ) {} + + private isVisibleToClientType(client: any, actor: any): boolean { + if (actor.userType === UserType.GENUINE) { + return true; + } + if ( + actor.userType === UserType.LEGAL && + String(client._id) === actor.clientKey + ) { + return true; + } + return false; + } + + private wasHandledByActor(request: any, actorSub: string): boolean { + type ActorCheckerEntry = { CheckedRequest?: { actorId: string } }; + const actorChecker = request.actorsChecker as ActorCheckerEntry[]; + + if (!Array.isArray(actorChecker)) { + return false; + } + + const matchingEntry = actorChecker.find( + (entry) => String(entry?.CheckedRequest?.actorId) === actorSub, + ); + + return !!matchingEntry; + } + + async getClaimRequestsListForExpert(actor): Promise { + const requests = await this.claimRequestManagementDbService.findAllByStatus( + { + claimStatus: { + $in: [ + ReqClaimStatus.UnChecked, + ReqClaimStatus.ReviewRequest, + ReqClaimStatus.CheckAgain, + ReqClaimStatus.CloseRequest, + ReqClaimStatus.InPersonVisit, + ReqClaimStatus.CheckedRequest, + ReqClaimStatus.PendingFactorValidation, + ], + }, + }, + ); + + const filteredRequests = []; + + for (const r of requests) { + // For expert-initiated blame files, only show to the initiating expert + if (r.blameFile?.expertInitiated && r.blameFile?.initiatedBy) { + if (String(r.blameFile.initiatedBy) !== actor.sub) { + continue; // Skip if not the initiating expert + } + // Expert-initiated claim files are always visible to the initiating expert + filteredRequests.push(r); + continue; + } + + const client = await this.clientDbService.findOne({ + _id: r.userClientKey, + }); + + if (!client) { + this.logger.warn( + `Client not found for claim request with ID: ${r._id}. Skipping.`, + ); + continue; + } + + const specialHandlingStatuses = [ + ReqClaimStatus.CheckAgain, + ReqClaimStatus.ReviewRequest, + ReqClaimStatus.PendingFactorValidation, + ]; + + const requiresSpecificActorCheck = specialHandlingStatuses.includes( + r.claimStatus, + ); + + if (requiresSpecificActorCheck) { + if (this.wasHandledByActor(r, actor.sub)) { + filteredRequests.push(r); + } + } else { + if (this.isVisibleToClientType(client, actor)) { + filteredRequests.push(r); + } + } + } + + if (filteredRequests.length === 0) { + throw new NotFoundException( + "No claim requests found for you at this time.", + ); + } + + return new ClaimListDtoRs(filteredRequests); + } + + public unlockApi(request, timer) { + // ADD to project + return setTimeout(() => { + this.claimRequestManagementDbService.findOne(request._id).then((r) => { + const updateExp = { + lockFile: false, + unlockTime: null, + actorLocked: {}, + }; + if (r?.claimStatus == ReqClaimStatus.ReviewRequest) { + Object.assign(updateExp, { + claimStatus: ReqClaimStatus.UnChecked, + }); + } + this.claimRequestManagementDbService.findOneAndUpdate(request._id, { + ...updateExp, + }); + }); + this.logger.log(`unlock this request : ${request._id}`); + // TODO enum request claimStatus create + }, timer); + } + + async lockClaimRequest(requestId: string, actorDetail) { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + if (!request) throw new BadRequestException("Claim request not found"); + + if (request.lockFile) { + throw new BadRequestException("Claim request is locked"); + } + + if (request.claimStatus === ReqClaimStatus.UserPending) { + throw new BadRequestException( + "Cannot lock request because claimStatus is UserPending", + ); + } + + if ( + request.damageExpertReplyFinal && + request.claimStatus === ReqClaimStatus.CheckedRequest + ) { + throw new BadRequestException("Request has damage expert reply"); + } + + const fifteenMinutes = new Date(Date.now() + 15 * 60 * 1000); + + await this.claimRequestManagementDbService.findOneAndUpdate( + new Types.ObjectId(requestId), + { + lockFile: true, + claimStatus: ReqClaimStatus.ReviewRequest, + unlockTime: fifteenMinutes, + lockTime: Date.now(), + $set: { + actorLocked: { + actorId: new Types.ObjectId(actorDetail.sub), + fullName: actorDetail.fullName, + }, + }, + $push: { + actorsChecker: { + [ReqClaimStatus.ReviewRequest]: { + fullName: actorDetail.fullName, + actorId: new Types.ObjectId(actorDetail.sub), + }, + Date: new Date(), + }, + }, + }, + ); + + await this.damageExpertDbService.updateStats( + actorDetail.sub, + "checked", + requestId, + ); + + this.unlockApi(request, fifteenMinutes.getTime() - Date.now()); + + return { _id: requestId, lock: true }; + } + + private normalizeText(str: string) { + if (!str || typeof str !== 'string') { + return ''; + } + return str + .replace(/[٠-٩۰-۹]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 1728)) + .replace(/[\u064B-\u065F]/g, "") + .replace(/\s+/g, " ") + .trim() + .toLowerCase(); + } + + normalizeKey(str: string): string { + return str.toLowerCase().replace(/[^a-z0-9]/g, ""); + } + + parsePersianNumber(input: string): number { + return Number( + input + .replace(/[۰-۹]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 1728)) + .replace(/,/g, ""), + ); + } + + private findClosestCar(input: string, cars: { carName: string }[]) { + if (!input || !cars || cars.length === 0) { + this.logger.debug( + `[PriceDrop] findClosestCar: Invalid input. input: ${input}, cars length: ${cars?.length || 0}`, + ); + return null; + } + const normalizedInput = this.normalizeText(input); + if (!normalizedInput) { + this.logger.debug( + `[PriceDrop] findClosestCar: Could not normalize input "${input}"`, + ); + return null; + } + let bestMatch = null; + let minDistance = Infinity; + let validCarsChecked = 0; + + for (const car of cars) { + if (!car || !car.carName) { + continue; + } + const normalizedCarName = this.normalizeText(car.carName); + if (!normalizedCarName) { + continue; + } + validCarsChecked++; + const dist = stringDistance(normalizedInput, normalizedCarName); + if (dist < minDistance) { + minDistance = dist; + bestMatch = car; + } + } + + if (!bestMatch) { + this.logger.debug( + `[PriceDrop] findClosestCar: No match found for "${input}" after checking ${validCarsChecked} valid cars out of ${cars.length} total`, + ); + } else { + this.logger.debug( + `[PriceDrop] findClosestCar: Best match for "${input}" is "${bestMatch.carName}" with distance ${minDistance}`, + ); + } + + return bestMatch; + } + + private priceDropFormula( + carPrice: number | string, + carModel: number, + carValue: number[], + ) { + let normalizePrice: number; + + if (typeof carPrice === "string") { + normalizePrice = this.parsePersianNumber(carPrice); + } else if (typeof carPrice === "number") { + normalizePrice = carPrice; + } else { + this.logger.warn( + `Invalid carPrice type received in formula: ${typeof carPrice}`, + ); + normalizePrice = 0; + } + + const coefficient = this.yearCoefficient[carModel] || 1; + const sumOfSeverity = carValue.reduce((a, c) => a + c, 0); + const totalDrop = (normalizePrice * coefficient * sumOfSeverity) / 400; + + return { + carPrice: normalizePrice, + carModel: carModel || null, + carValue: carValue || [], + sumOfSeverity: sumOfSeverity || 0, + coefficientYear: coefficient || 1, + total: totalDrop || 0, + }; + } + + async calculatePriceDrop(request: any, carPartsAi: any, query?: any) { + const requestId = request?._id?.toString() || 'unknown'; + + try { + // Step 1: Check SandHub data + if (!request?.blameFile?.sanHubId) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Missing sanHubId in blameFile. Cannot calculate price drop.`, + ); + return; + } + + const sandHubData = await this.sandHubService.getSandHubDataFromId( + request.blameFile.sanHubId, + ); + if (!sandHubData) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Could not retrieve SandHub data for sanHubId: ${request.blameFile.sanHubId}. Cannot calculate price drop.`, + ); + return; + } + + if (sandHubData.MapTypNam === undefined || sandHubData.MapTypNam === null) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: SandHub data missing ModelCii. Cannot calculate price drop.`, + ); + return; + } + + const existingPriceDrop = request.priceDrop || {}; + const manualOverride = query || {}; + + // Step 2: Check car parts and get severity values + if (!carPartsAi || (Array.isArray(carPartsAi) && carPartsAi.length === 0)) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: No car parts data provided. Cannot calculate severity values.`, + ); + } + + const severityValues = this.getSeverityValues(carPartsAi); + let autoCalculatedData: any = {}; + + if (severityValues.length === 0) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: No severity values found from car parts. Price drop calculation will use existing data or manual override only.`, + ); + } + + // Step 3: Check car detail and find closest car + if (!request?.carDetail) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Missing carDetail in request. Cannot find car price.`, + ); + } else if (!request.carDetail.carName) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Missing carName in carDetail. Cannot find car price.`, + ); + } else if (severityValues.length > 0) { + // Only fetch car prices if we have severity values to calculate + const carPrices = await this.fetchCarPrices(); + + if (!carPrices || carPrices.length === 0) { + this.logger.error( + `[PriceDrop] Request ${requestId}: Failed to fetch car prices or car prices list is empty. Cannot find matching car.`, + ); + } else { + this.logger.debug( + `[PriceDrop] Request ${requestId}: Fetched ${carPrices.length} car prices. Searching for: "${request.carDetail.carName}"`, + ); + + const closestCar = this.findClosestCar( + request.carDetail.carName, + carPrices, + ); + + if (!closestCar) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Could not find closest matching car for "${request.carDetail.carName}". Available cars: ${carPrices.length}. Cannot determine car price.`, + ); + } else if (!closestCar.marketPrice) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Found matching car "${closestCar.carName}" but marketPrice is missing. Cannot calculate price drop.`, + ); + } else { + this.logger.debug( + `[PriceDrop] Request ${requestId}: Found matching car "${closestCar.carName}" with marketPrice: ${closestCar.marketPrice}`, + ); + autoCalculatedData = { + carPrice: closestCar.marketPrice, + carValue: severityValues, + }; + } + } + } + + // Step 4: Merge and recalculate + const finalPriceDropData = this.mergeAndRecalculatePriceDrop( + existingPriceDrop, + autoCalculatedData, + manualOverride, + sandHubData.ModelCii, + ); + + if (!finalPriceDropData) { + this.logger.error( + `[PriceDrop] Request ${requestId}: Failed to generate final price drop data.`, + ); + return; + } + + // Step 5: Log calculation result + if (!finalPriceDropData.carPrice || !finalPriceDropData.carValue || finalPriceDropData.carValue.length === 0) { + this.logger.warn( + `[PriceDrop] Request ${requestId}: Price drop calculated but missing critical data. ` + + `carPrice: ${finalPriceDropData.carPrice}, ` + + `carValue length: ${finalPriceDropData.carValue?.length || 0}, ` + + `total: ${finalPriceDropData.total}`, + ); + } else { + this.logger.debug( + `[PriceDrop] Request ${requestId}: Successfully calculated price drop. ` + + `carPrice: ${finalPriceDropData.carPrice}, ` + + `sumOfSeverity: ${finalPriceDropData.sumOfSeverity}, ` + + `total: ${finalPriceDropData.total}`, + ); + } + + // Step 6: Save to database + await this.claimRequestManagementDbService.findOneAndUpdate( + { _id: new Types.ObjectId(request._id) }, + { $set: { priceDrop: finalPriceDropData } }, + ); + + this.logger.log( + `[PriceDrop] Request ${requestId}: Price drop calculation completed and saved.`, + ); + } catch (err) { + this.logger.error( + `[PriceDrop] Request ${requestId}: An error occurred during the price drop calculation: ${err.message}`, + err.stack, + ); + } + } + + private mergeAndRecalculatePriceDrop( + existingData: any, + autoCalculatedData: any, + manualOverride: any, + carModel: number, + ) { + const mergedInputs = { + ...existingData, + ...autoCalculatedData, + ...manualOverride, + }; + + if (!mergedInputs.carPrice || !mergedInputs.carValue) { + return { + carPrice: mergedInputs.carPrice || null, + carModel: carModel, + carValue: mergedInputs.carValue || [], + sumOfSeverity: 0, + coefficientYear: this.yearCoefficient[carModel] || 1, + total: 0, + }; + } + + return this.priceDropFormula( + mergedInputs.carPrice, + carModel, + mergedInputs.carValue, + ); + } + + private getSeverityValues(carPartsAi: any[]): number[] { + const severities: number[] = []; + + const knownPartKeyMap = new Map(); + for (const originalKey of Object.keys(this.priceDropPart)) { + if (typeof originalKey === "string") { + knownPartKeyMap.set(this.normalizeKey(originalKey), originalKey); + } + } + + for (const part of carPartsAi) { + const damagedPartTable = part?.aiReport?.damaged_part_table; + if (Array.isArray(damagedPartTable) && damagedPartTable.length > 0) { + for (const damagedPartInfo of damagedPartTable) { + if (damagedPartInfo && typeof damagedPartInfo.name === "string") { + const normalizedAiPartName = this.normalizeKey( + damagedPartInfo.name, + ); + + if (knownPartKeyMap.has(normalizedAiPartName)) { + const originalPriceDropKey = + knownPartKeyMap.get(normalizedAiPartName); + + const severityValue = + this.priceDropPart[originalPriceDropKey]?.[ + damagedPartInfo.severity + ]; + + if (severityValue !== undefined) { + severities.push(severityValue); + } + } + } + } + } + } + return severities; + } + + private async fetchCarPrices(): Promise< + { carName: string; marketPrice: number }[] + > { + const carPrices: { carName: string; marketPrice: number }[] = []; + const endpoints = ["akharin", "hamrah"]; + + for (const item of endpoints) { + try { + const url = `${process.env.CW_URL}price?${item}`; + const response = await lastValueFrom( + this.httpService.get(url), + ); + if (Array.isArray(response.data)) { + let validEntries = 0; + for (const r of response.data) { + if (r?.carName && r?.marketPrice) { + carPrices.push({ + carName: r.carName, + marketPrice: r.marketPrice, + }); + validEntries++; + } + } + this.logger.debug( + `[PriceDrop] Fetched ${validEntries} valid car prices from endpoint '${item}' (total entries: ${response.data.length})`, + ); + } else { + this.logger.warn( + `[PriceDrop] Endpoint '${item}' returned non-array data: ${typeof response.data}`, + ); + } + } catch (err) { + this.logger.error( + `[PriceDrop] Error fetching car prices for endpoint '${item}': ${err.message}`, + err.stack, + ); + } + } + + if (carPrices.length === 0) { + this.logger.error( + `[PriceDrop] No car prices fetched from any endpoint. Endpoints tried: ${endpoints.join(', ')}`, + ); + } else { + this.logger.debug( + `[PriceDrop] Total car prices fetched: ${carPrices.length}`, + ); + } + + return carPrices; + } + + private calculateFinalDropPrice( + closestCar: { marketPrice: number } | null, + carModel: any, + severities: number[], + query?: any, + ) { + if (query?.carPrice && query?.carValue) { + return this.priceDropFormula(query.carPrice, carModel, query.carValue); + } + if (closestCar?.marketPrice && severities.length > 0) { + return this.priceDropFormula( + closestCar.marketPrice, + carModel, + severities, + ); + } + return null; + } + + async requestPerId(requestId: string, currentUser: any, query?: any) { + try { + // 1. Fetch the initial request document and perform calculations + const initialRequest = + await this.claimRequestManagementDbService.findOneDocument(requestId); + + if (!initialRequest) { + throw new HttpException( + { responseCode: 1006, message: "Request not found" }, + HttpStatus.NOT_FOUND, + ); + } + + await this.calculatePriceDrop( + initialRequest, + initialRequest.imageRequired.selectPartOfCar, + query, + ); + + const requestUpdated = + await this.claimRequestManagementDbService.findOneDocument(requestId); + + await this.populateFactorLinks(requestUpdated.damageExpertReply); + await this.populateFactorLinks(requestUpdated.damageExpertReplyFinal); + + // 2. Populate required documents with file links + if (requestUpdated.requiredDocuments && Object.keys(requestUpdated.requiredDocuments).length > 0) { + for (const documentType in requestUpdated.requiredDocuments) { + const documentId = requestUpdated.requiredDocuments[documentType]; + if (documentId) { + try { + const doc = await this.claimRequiredDocumentDbService.findById( + documentId.toString(), + ); + if (doc && doc.path) { + // Replace ID with file URL + requestUpdated.requiredDocuments[documentType] = buildFileLink(doc.path) as any; + } + } catch (error) { + this.logger.warn( + `Failed to populate required document ${documentType}: ${error.message}`, + ); + // Keep the ID if population fails + } + } + } + } + + // 3. Populate the resent document and voice links from the nested blameFile. + if (requestUpdated.blameFile?.expertResendReply) { + // Helper function to avoid repeating code + const populatePartyLinks = async ( + partyKey: "firstParty" | "secondParty", + ) => { + const partyReply = + requestUpdated.blameFile.expertResendReply[partyKey]; + if (!partyReply) return; + + // Populate the voice link + if (partyReply.voice) { + const voiceDoc = await this.blameVoiceDbService.findOne( + partyReply.voice.toString(), + ); + if (voiceDoc) { + partyReply.voice = buildFileLink(voiceDoc.path); + } + } + + // Populate the document links + if (partyReply.documents) { + for (const docType in partyReply.documents) { + const docId = partyReply.documents[docType]; + if (docId) { + // This line will now work correctly after you inject the service. + const doc = await this.blameDocumentDbService.findById( + docId.toString(), + ); + if (doc) { + partyReply.documents[docType] = buildFileLink(doc.path); // Replace ID with URL + } + } + } + } + }; + + // Run the population for both parties + await populatePartyLinks("firstParty"); + await populatePartyLinks("secondParty"); + } + + // 4. Perform authorization checks (as before) + if (this.isCurrentUserAllowed(requestUpdated, currentUser)) { + return new ClaimPerIdRs(requestUpdated); + } + + if (this.isRequestLocked(requestUpdated)) { + throw new HttpException( + { responseCode: 1007, message: "Request is locked" }, + HttpStatus.FORBIDDEN, + ); + } + + // 5. Return the fully populated response + return new ClaimPerIdRs(requestUpdated); + } catch (error) { + this.globalErrHandler(error); + } + } + + async imageRequired(requestId, currentUser) { + const request = + await this.claimRequestManagementDbService.findOneDocument(requestId); + + if (!request) { + throw new HttpException( + { responseCode: 1006, message: "Request not found" }, + HttpStatus.NOT_FOUND, + ); + } + + if (this.isCurrentUserAllowed(request, currentUser)) { + return (await this.claimRequestManagementDbService.findOne(requestId)) + .imageRequired; + } else if (this.isRequestLocked(requestId)) { + throw new HttpException( + { responseCode: 1007, message: "Request is locked" }, + HttpStatus.FORBIDDEN, + ); + } else { + throw new HttpException( + { responseCode: 1008, message: "notAllowed is locked" }, + HttpStatus.FORBIDDEN, + ); + } + } + + private async populateFactorLinks(reply: any): Promise { + if (!reply || !Array.isArray(reply.parts)) { + return; + } + + for (const part of reply.parts) { + if (part.factorLink && Types.ObjectId.isValid(part.factorLink)) { + const factorDoc = await this.claimFactorsImageDbService.findById( + part.factorLink.toString(), + ); + + if (factorDoc) { + part.factorLink = buildFileLink(factorDoc.path); + } + } + } + } + + async getRequiredDocuments(requestId: string, currentUser: any) { + try { + const request = + await this.claimRequestManagementDbService.findOneDocument(requestId); + + if (!request) { + throw new HttpException( + { responseCode: 1006, message: "Request not found" }, + HttpStatus.NOT_FOUND, + ); + } + + // Perform authorization checks + if (this.isRequestLocked(request) && !this.isCurrentUserAllowed(request, currentUser)) { + throw new HttpException( + { responseCode: 1007, message: "Request is locked" }, + HttpStatus.FORBIDDEN, + ); + } + + // Fetch all required documents for this claim + const documents = await this.claimRequiredDocumentDbService.findByClaimId( + requestId, + ); + + // Populate with file URLs + const populatedDocuments = documents.map((doc) => ({ + _id: doc._id, + documentType: doc.documentType, + fileName: doc.fileName, + fileUrl: buildFileLink(doc.path), + uploadedAt: doc.uploadedAt, + })); + + return { + claimId: requestId, + totalDocuments: populatedDocuments.length, + documents: populatedDocuments, + }; + } catch (error) { + this.globalErrHandler(error); + } + } + + private async processAiImages( + imageRequired: any, + ): Promise<{ aroundTheCar: any[]; damagePartOfCar: any[] }> { + const aiImages = { aroundTheCar: [], damagePartOfCar: [] }; + return aiImages; + } + + private async processImage(imageId: string) { + return this.damageImageDbService.findOne(imageId); + } + + private isCurrentUserAllowed(request: any, currentUser: any): boolean { + return ( + String(request?.actorLocked?.actorId) === currentUser.sub && + request.lockFile + ); + } + + private isRequestLocked(request: any): boolean { + return ( + request.lockFile && request.claimStatus === ReqClaimStatus.ReviewRequest + ); + } + + async submitReplyRequest( + requestId: string, + reply: ClaimSubmitReplyDto, + userId: any, + ) { + try { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + + if (!request) { + throw new NotFoundException("Request not found"); + } + if ( + String(request?.actorLocked?.actorId) !== userId.sub && + !request?.objection + ) { + throw new ForbiddenException("Access denied to this request"); + } + if (request?.unlockTime == null && !request?.objection) { + throw new ForbiddenException("Your time has expired"); + } + if (!request.lockFile && !request?.objection) { + throw new ForbiddenException( + "For submit reply you must lock the request", + ); + } + + // Validate total price cap (30,000,000) + if (reply.parts && reply.parts.length > 0) { + const PRICE_CAP = 30000000; + let totalPrice = 0; + + for (const part of reply.parts) { + if (part.totalPayment) { + let partPrice: number; + if (typeof part.totalPayment === "string") { + // Handle Persian numbers and remove commas + partPrice = this.parsePersianNumber(part.totalPayment); + } else if (typeof part.totalPayment === "number") { + partPrice = part.totalPayment; + } else { + this.logger.warn( + `Invalid totalPayment type for part ${part.partId}: ${typeof part.totalPayment}`, + ); + partPrice = 0; + } + + if (isNaN(partPrice)) { + this.logger.warn( + `Could not parse totalPayment for part ${part.partId}: ${part.totalPayment}`, + ); + partPrice = 0; + } + + totalPrice += partPrice; + } + } + + if (totalPrice > PRICE_CAP) { + throw new BadRequestException({ + message: `[PRICE_CAP_ERROR] Total price of damaged parts (${totalPrice.toLocaleString()}) exceeds the maximum allowed amount (${PRICE_CAP.toLocaleString()}). Please adjust the prices.`, + error: "PRICE_CAP_ERROR", + code: "PRICE_CAP_ERROR", + totalPrice: totalPrice, + priceCap: PRICE_CAP, + }); + } + } + + // Validate daghi fields + if (reply.parts && reply.parts.length > 0) { + for (const part of reply.parts) { + if (!part.daghi || !part.daghi.option) { + throw new BadRequestException( + `Daghi option is required for part ${part.partId}`, + ); + } + + // Validate based on option + if (part.daghi.option === DaghiOption.RECYCLED_PARTS_VALUE) { + if (!part.daghi.price) { + throw new BadRequestException( + `Price is required for part ${part.partId} when option is '${DaghiOption.RECYCLED_PARTS_VALUE}'`, + ); + } + } else if (part.daghi.option === DaghiOption.DELIVER_DAMAGED_PART) { + if (!part.daghi.branchId) { + throw new BadRequestException( + `Branch ID is required for part ${part.partId} when option is '${DaghiOption.DELIVER_DAMAGED_PART}'`, + ); + } + // Validate branchId is a valid ObjectId + if (!Types.ObjectId.isValid(part.daghi.branchId)) { + throw new BadRequestException( + `Invalid branch ID format for part ${part.partId}`, + ); + } + } + // For NO_VALUE and WITH_DAMAGED_PART_CALCULATION, no additional fields needed + } + } + + const isObjection = !!request.objection; + const replyFieldKey = isObjection + ? "damageExpertReplyFinal" + : "damageExpertReply"; + + const needsFactorUpload = + reply.parts?.some((part) => part.factorNeeded === true) ?? false; + + const nextStatus = needsFactorUpload + ? ReqClaimStatus.PendingFactorUpload + : ReqClaimStatus.CheckedRequest; + + const nextStep = needsFactorUpload + ? ClaimStepsEnum.WaitingForFactorUpload + : ClaimStepsEnum.WaitingForUserToReact; + + // Process parts: convert branchId string to ObjectId if present + const processedParts = reply.parts.map((part) => ({ + ...part, + daghi: { + option: part.daghi.option, + ...(part.daghi.price && { price: part.daghi.price }), + ...(part.daghi.branchId && { + branchId: new Types.ObjectId(part.daghi.branchId), + }), + }, + })); + + const replyPayload = { + description: reply.description, + parts: processedParts, + submitTime: new Date(), + actorDetail: { + actorName: userId.fullName, + actorId: userId.sub, + }, + }; + + const updatePayload = { + $set: { + lockFile: false, + claimStatus: nextStatus, + currentStep: nextStep, + [replyFieldKey]: replyPayload, + }, + $push: { + actorsChecker: { + [ReqClaimStatus.CheckedRequest]: request.actorLocked, + Date: new Date(), + }, + steps: nextStep, + }, + }; + + await this.claimRequestManagementDbService.findAndUpdate( + requestId, + updatePayload, + ); + + if (!isObjection) { + await this.damageExpertDbService.updateStats( + userId.sub, + "handled", + requestId, + ); + } + + return { + requestId: request._id, + claimStatus: nextStatus, + }; + } catch (error) { + this.logger.error( + `Error in submitReplyRequest for claim ${requestId}:`, + error.stack, + ); + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + "An internal server error occurred.", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async submitResend( + requestId: string, + body: ClaimSubmitResendDto, + userId: any, + ) { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + + if (!request) throw new NotFoundException("request_not_found"); + if (String(request?.actorLocked?.actorId) !== userId) + throw new ForbiddenException("access_denied_please_lock"); + if (request.unlockTime == null) + throw new ForbiddenException("time_expired"); + if (!request.lockFile) + throw new ForbiddenException( + "for submit reply you must lock the request", + ); + if (request.damageExpertResend) + throw new ForbiddenException("request already has resend reply"); + if (request.damageExpertReplyFinal) + throw new ForbiddenException("request already has final reply"); + + await this.claimRequestManagementDbService.findAndUpdate(requestId, { + lockFile: false, + claimStatus: ReqClaimStatus.WaitingForUserToResend, + damageExpertResend: { + resendDescription: body.resendDescription, + resendDocuments: body.resendDocuments, + resendCarParts: body.resendCarParts, + }, + $push: { + actorsChecker: { + [ReqClaimStatus.WaitingForUserToResend]: request.actorLocked, + }, + Date: new Date(), + }, + }); + + return request; + } + + async streamService(requestId, query, res, header) { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + let videoPath: string = ""; + switch (query) { + case "accident": + videoPath = await this.getAccidentVideoPath( + String( + request?.blameFile?.firstPartyDetails?.firstPartyFile + ?.firstPartyVideoId, + ), + ); + break; + case "car-capture": + videoPath = await this.getCarCapture(requestId); + break; + default: + throw new NotFoundException("Invalid query type"); + } + if (!videoPath) throw new NotFoundException("video_not_found"); + + const absolutePath = join(process.cwd(), videoPath.replace(/^\/+/, "")); + + if (!existsSync(absolutePath)) { + throw new NotFoundException( + `Video file not found at path: ${absolutePath}`, + ); + } + + const { size } = statSync(absolutePath); + const range = header.range; + + if (range) { + const [startStr, endStr] = range.replace(/bytes=/, "").split("-"); + const start = parseInt(startStr, 10); + const end = endStr ? parseInt(endStr, 10) : size - 1; + const chunkSize = end - start + 1; + + const file = createReadStream(absolutePath, { start, end }); + + res.writeHead(206, { + "Content-Range": `bytes ${start}-${end}/${size}`, + "Accept-Ranges": "bytes", + "Content-Length": chunkSize, + "Content-Type": "video/mp4", + }); + + file.pipe(res); + } else { + res.writeHead(200, { + "Content-Length": size, + "Content-Type": "video/mp4", + }); + + createReadStream(absolutePath).pipe(res); + } + } + + async getVideoLink( + requestId: string, + query: "car-capture" | "accident", + ): Promise<{ url: string }> { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Claim request not found"); + } + + let videoPath: string = ""; + switch (query) { + case "accident": + videoPath = await this.getAccidentVideoPath( + String( + request?.blameFile?.firstPartyDetails?.firstPartyFile + ?.firstPartyVideoId, + ), + ); + break; + case "car-capture": + videoPath = await this.getCarCapture(requestId); + break; + default: + throw new BadRequestException("Invalid query type specified"); + } + + if (!videoPath) { + throw new NotFoundException( + "Video file not found for the specified type.", + ); + } + + // Use your existing helper to build the full, public URL + const fileUrl = buildFileLink(videoPath); + + // Return the URL in a clean JSON object + return { url: fileUrl }; + } + + async voiceStream(id, res, headers) { + // audio/mpeg + const voice = await this.blameVoiceDbService.findOne(id); + if (!voice) throw new NotFoundException("video_not_found"); + const { size } = statSync(voice.path); + const voicePath = voice.path; + const VoiceRange = headers.range; + if (VoiceRange) { + const parts = VoiceRange?.replace(/bytes=/, "").split("-"); + const end = parts[1] ? parseInt(parts[1], 16) : size - 1; + const start = parseInt(parts[0], 10); + const chunksize = end - start + 1; + + const file = createReadStream(voicePath, { start, end }); + + const header = { + "Content-Range": `bytes ${start}-${end}/${size}`, + "Accept-Ranges": "bytes", + "Content-Length": chunksize, + "Content-Type": "audio/mpeg", + }; + + res.writeHead(206, header); + file.pipe(res); + } else { + const head = { + "Content-Length": size, + "Content-Type": "audio/mpeg", + }; + res.writeHead(200, head); + createReadStream(voicePath).pipe(res); + } + } + + async getVoiceLink(id: string): Promise<{ url: string }> { + const voice = await this.blameVoiceDbService.findOne(id); + if (!voice) { + throw new NotFoundException("Voice file not found"); + } + + const fileUrl = buildFileLink(voice.path); + + return { url: fileUrl }; + } + + async getCarCapture(requestId): Promise { + const video = await this.claimVideoCaptureDbService.findOne({ + claimId: new Types.ObjectId(requestId), + }); + + if (!video?.path) { + throw new NotFoundException("Car capture video not found"); + } + + return String(video.path); + } + + async getAccidentVideoPath(requestId: string): Promise { + const video = await this.blameVideoDbService.findOneByRequestId(requestId); + if (!video?.path) { + throw new NotFoundException("Accident video not found"); + } + + return video.path; + } + + async validateClaimFactors( + claimId: string, + decisions: { + partId: string; + status: FactorStatus; + rejectionReason?: string; + }[], + expertId: string, + ) { + const claim = await this.claimRequestManagementDbService.findOne(claimId); + if (!claim) { + throw new NotFoundException("Claim not found"); + } + + const finalReply = claim.damageExpertReplyFinal || claim.damageExpertReply; + const replyFieldKey = claim.damageExpertReplyFinal + ? "damageExpertReplyFinal" + : "damageExpertReply"; + + if (!finalReply) { + throw new BadRequestException("No expert reply found in this claim."); + } + + for (const decision of decisions) { + const partIndex = finalReply.parts.findIndex( + (p) => p.partId === decision.partId, + ); + if (partIndex === -1) { + this.logger.warn( + `Part with ID ${decision.partId} not found in claim ${claimId}. Skipping.`, + ); + continue; + } + + await this.claimRequestManagementDbService.findOneAndUpdate( + { _id: new Types.ObjectId(claimId) }, + { + $set: { + [`${replyFieldKey}.parts.${partIndex}.factorStatus`]: + decision.status, + [`${replyFieldKey}.parts.${partIndex}.rejectionReason`]: + decision.rejectionReason || null, + }, + }, + ); + } + + const updatedClaim = + await this.claimRequestManagementDbService.findOne(claimId); + const updatedReply = + updatedClaim.damageExpertReplyFinal || updatedClaim.damageExpertReply; + + const requiredFactors = updatedReply.parts.filter((p) => p.factorNeeded); + + const anyRejected = requiredFactors.some( + (p) => p.factorStatus === FactorStatus.REJECTED, + ); + const anyStillPending = requiredFactors.some( + (p) => p.factorStatus === FactorStatus.PENDING || !p.factorStatus, + ); + + let newStatus: ReqClaimStatus | null = null; + let responseMessage = "Factor validation complete."; + + if (anyRejected) { + // At least one factor is rejected → stay in factor flow and ask user to re-upload + newStatus = ReqClaimStatus.FactorRejected; + responseMessage = + "Factors reviewed. Some factors were rejected, awaiting user resubmission."; + // TODO: Send an SMS or notification to the user here. + } else if (anyStillPending) { + // Some factors still pending (e.g. not validated in this batch) + responseMessage = + "Some factors were approved, but others are still pending validation."; + } else { + // All required factors are approved: + // Move claim into a state where the user can review the final decision and object if needed. + newStatus = ReqClaimStatus.CheckedRequest; + responseMessage = + "All required factors have been approved. The claim is now ready for user review and possible objection."; + } + + if (newStatus) { + const update: any = { + $set: { claimStatus: newStatus }, + }; + + // When factors are fully approved, also move the step to WaitingForUserToReact, + // just like the non-factor flow in submitReplyRequest. + if (newStatus === ReqClaimStatus.CheckedRequest) { + update.$set.currentStep = ClaimStepsEnum.WaitingForUserToReact; + update.$set.nextStep = ClaimStepsEnum.WaitingForUserToReact; + update.$push = { + steps: ClaimStepsEnum.WaitingForUserToReact, + }; + } + + await this.claimRequestManagementDbService.findByIdAndUpdate( + claimId, + update, + ); + } + + return { + message: responseMessage, + newStatus: newStatus || updatedClaim.claimStatus, + }; + } + + async inPersonVisit(requestId: string, actorDetail: any) { + const request = + await this.claimRequestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Blame not found"); + } + + const updated = await this.claimRequestManagementDbService.findAndUpdate( + requestId, + { + claimStatus: ReqClaimStatus.InPersonVisit, + }, + ); + + await this.damageExpertDbService.updateStats( + actorDetail.sub, + "handled", + requestId, + ); + + return updated; + } + + async retrieveInsuranceBranches(insuranceId: string) { + return await this.branchDbService.findAll(insuranceId); + } + + private globalErrHandler(error) { + this.logger.error(error.response?.responseCode, error.response); + throw new HttpException(error.response, error.claimStatus); + } +} diff --git a/src/expert-insurer/expert-insurer.controller.ts b/src/expert-insurer/expert-insurer.controller.ts new file mode 100644 index 0000000..c0ae414 --- /dev/null +++ b/src/expert-insurer/expert-insurer.controller.ts @@ -0,0 +1,154 @@ +import { + BadRequestException, + Body, + Controller, + Get, + Param, + Post, + Put, + Query, + UnauthorizedException, + UseGuards, +} from "@nestjs/common"; +import { + ApiBearerAuth, + ApiBody, + ApiParam, + ApiQuery, + ApiTags, +} from "@nestjs/swagger"; +import { Types } from "mongoose"; +import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { Roles } from "src/decorators/roles.decorator"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { FileRating } from "src/request-management/entities/schema/request-management.schema"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { ExpertInsurerService } from "./expert-insurer.service"; +import { CreateBranchDto } from "src/client/dto/create-branch.dto"; + +@Controller("expert-insurer") +@ApiTags("expert-insurer-panel") +@ApiBearerAuth() +@UseGuards(LocalActorAuthGuard, RolesGuard) +@Roles(RoleEnum.COMPANY) +export class ExpertInsurerController { + constructor(private readonly expertInsurerService: ExpertInsurerService) {} + + @Get("files") + async getAllFiles(@CurrentUser() insurer) { + return await this.expertInsurerService.retrieveAllFilesOfClient( + insurer.clientKey, + ); + } + + @ApiQuery({ name: "page", type: Number }) + @ApiQuery({ name: "response_count", type: Number }) + @Get("list") + async getAllExperts( + @Query("page") page: number, + @Query("response_count") count: number, + @CurrentUser() actor, + ) { + return await this.expertInsurerService.retrieveAllExpertsOfClient( + actor, + page, + count, + ); + } + + @ApiParam({ name: "expertId" }) + @ApiQuery({ name: "role", enum: ["claim", "blame"] }) + @Get("/:expertId") + async requestDetail( + @Param("expertId") id: string, + @Query("role") role: "claim" | "blame", + ) { + if (!Types.ObjectId.isValid(new Types.ObjectId(id))) { + throw new BadRequestException("Invalid expert ID"); + } + + if (!["claim", "blame"].includes(role)) { + throw new BadRequestException("Invalid role"); + } + + return await this.expertInsurerService.getAllFilesByExpertAndRole(id, role); + } + + @ApiBody({ + description: "Detailed expert rating", + schema: { + type: "object", + properties: { + collisionMethodAccuracy: { + type: "number", + minimum: 0, + maximum: 5, + example: 4, + description: "تشخیص درست نحوه برخورد", + }, + evaluationTimeliness: { + type: "number", + minimum: 0, + maximum: 5, + example: 3, + description: "زمان ارزیابی", + }, + accidentCauseAccuracy: { + type: "number", + minimum: 0, + maximum: 5, + example: 5, + description: "تشخیص درست علت تصادف", + }, + guiltyVehicleIdentification: { + type: "number", + minimum: 0, + maximum: 5, + example: 4, + description: "تشخیص درست وسیله نقلیه مقصر", + }, + }, + required: [ + "collisionMethodAccuracy", + "evaluationTimeliness", + "accidentCauseAccuracy", + "guiltyVehicleIdentification", + ], + }, + }) + @ApiParam({ name: "requestId" }) + @ApiQuery({ name: "role", enum: ["claim", "blame"] }) + @Put("/:requestId/rating") + async rateExperts( + @Param("requestId") requestId: string, + @Body() rating: FileRating, + @Query("role") role: "claim" | "blame", + ) { + if (!["claim", "blame"].includes(role)) { + throw new BadRequestException("Invalid role"); + } + + return await this.expertInsurerService.rateExpertOnFile( + requestId, + rating, + role, + ); + } + + @Post("branches") + @ApiBody({ type: CreateBranchDto }) + async addBranch( + @CurrentUser() insurer, + @Body() createBranchDto: CreateBranchDto, + ) { + if (!insurer) { + throw new UnauthorizedException("Could not identify the current user."); + } + + return await this.expertInsurerService.addBranch( + insurer.clientKey, + createBranchDto, + ); + } +} diff --git a/src/expert-insurer/expert-insurer.module.ts b/src/expert-insurer/expert-insurer.module.ts new file mode 100644 index 0000000..0546bb4 --- /dev/null +++ b/src/expert-insurer/expert-insurer.module.ts @@ -0,0 +1,48 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { AuthModule } from "src/auth/auth.module"; +import { + ClaimRequestManagementModel, + ClaimRequestManagementSchema, +} from "src/claim-request-management/entites/schema/claim-request-management.schema"; +import { ClaimRequestManagementModule } from "src/claim-request-management/claim-request-management.module"; +import { ClientModule } from "src/client/client.module"; +import { + RequestManagementModel, + RequestManagementSchema, +} from "src/request-management/entities/schema/request-management.schema"; +import { RequestManagementModule } from "src/request-management/request-management.module"; +import { UsersModule } from "src/users/users.module"; +import { ExpertInsurerController } from "./expert-insurer.controller"; +import { ExpertInsurerService } from "./expert-insurer.service"; +import { + BranchModel, + BranchSchema, +} from "src/client/entities/schema/branch.schema"; + +@Module({ + imports: [ + ClaimRequestManagementModule, + RequestManagementModule, + AuthModule, + UsersModule, + ClientModule, + MongooseModule.forFeature([ + { + name: ClaimRequestManagementModel.name, + schema: ClaimRequestManagementSchema, + }, + { + name: RequestManagementModel.name, + schema: RequestManagementSchema, + }, + { + name: BranchModel.name, + schema: BranchSchema, + }, + ]), + ], + providers: [ExpertInsurerService], + controllers: [ExpertInsurerController], +}) +export class ExpertInsurerModule {} diff --git a/src/expert-insurer/expert-insurer.service.ts b/src/expert-insurer/expert-insurer.service.ts new file mode 100644 index 0000000..afc828a --- /dev/null +++ b/src/expert-insurer/expert-insurer.service.ts @@ -0,0 +1,481 @@ +import { + BadRequestException, + ConflictException, + Injectable, + Logger, + NotFoundException, +} from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types } from "mongoose"; +import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service"; +import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema"; +import { CreateBranchDto } from "src/client/dto/create-branch.dto"; +import { BranchDbService } from "src/client/entities/db-service/branch.db.service"; +import { ClientDbService } from "src/client/entities/db-service/client.db.service"; +import { buildFileLink } from "src/helpers/urlCreator"; +import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service"; +import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service"; +import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service"; +import { UserSignDbService } from "src/request-management/entities/db-service/sign.db.service"; +import { + FileRating, + RequestManagementModel, +} from "src/request-management/entities/schema/request-management.schema"; +import { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service"; +import { ExpertDbService } from "src/users/entities/db-service/expert.db.service"; + +@Injectable() +export class ExpertInsurerService { + private readonly logger = new Logger(ExpertInsurerService.name); + + constructor( + private readonly client: ClientDbService, + private readonly expertDbService: ExpertDbService, + private readonly damageExpertDbService: DamageExpertDbService, + @InjectModel(ClaimRequestManagementModel.name) + private readonly claimRequestManagementModel: Model, + @InjectModel(RequestManagementModel.name) + private readonly requestManagementModel: Model, + private readonly blameVoiceDbService: BlameVoiceDbService, + private readonly blameVideoDbService: BlameVideoDbService, + private readonly blameDocumentDbService: BlameDocumentDbService, + private readonly userSignDbService: UserSignDbService, + private readonly claimVideoCaptureDbService: VideoCaptureDbService, + private readonly branchDbService: BranchDbService, + ) {} + + async retrieveAllExpertsOfClient( + actor, + currentPage: number, + countPerPage: number, + ) { + const { clientKey } = actor; + if (!clientKey) return; + + const clientObjectId = new Types.ObjectId(clientKey); + + const [experts, damageExperts] = await Promise.all([ + this.expertDbService.findAll({ clientKey: clientKey }), + this.damageExpertDbService.findAll({ clientKey: clientObjectId }), + ]); + const allExpertsRaw = [...experts, ...damageExperts]; + const expertIds = allExpertsRaw.map((expert) => expert._id.toString()); + + if (expertIds.length === 0) + return { total: 0, page: currentPage, countPerPage, experts: [] }; + + const expertObjectIds = expertIds.map((id) => new Types.ObjectId(id)); + + const blameFileQuery = { + $and: [ + { + $or: [ + { "firstPartyDetails.firstPartyClient.clientId": clientObjectId }, + { "secondPartyDetails.secondPartyClient.clientId": clientObjectId }, + ], + }, + { "actorLocked.actorId": { $in: expertObjectIds } }, + ], + }; + + const claimFileQuery = { + $and: [ + { userClientKey: clientObjectId }, + { "damageExpertReply.actorDetail.actorId": { $in: expertIds } }, + ], + }; + + const [blameFiles, claimFiles] = await Promise.all([ + this.requestManagementModel.find(blameFileQuery).lean(), + this.claimRequestManagementModel.find(claimFileQuery).lean(), + ]); + + const expertTotalRatingsMap: Record = {}; + const expertRatingsByCategoryMap: Record< + string, + Record + > = {}; + + const processRatings = (expertId: string, rating: any) => { + if (!expertId || !rating || typeof rating !== "object") { + return; + } + + const allRatingValues = Object.values(rating).filter( + (val): val is number => typeof val === "number" && !isNaN(val), + ); + if (allRatingValues.length > 0) { + if (!expertTotalRatingsMap[expertId]) + expertTotalRatingsMap[expertId] = []; + expertTotalRatingsMap[expertId].push(...allRatingValues); + } + + if (!expertRatingsByCategoryMap[expertId]) { + expertRatingsByCategoryMap[expertId] = {}; + } + for (const [category, value] of Object.entries(rating)) { + if (typeof value === "number" && !isNaN(value)) { + if (!expertRatingsByCategoryMap[expertId][category]) { + expertRatingsByCategoryMap[expertId][category] = []; + } + expertRatingsByCategoryMap[expertId][category].push(value); + } + } + }; + + for (const file of blameFiles) { + const expertId = file?.actorLocked?.actorId?.toString(); + processRatings(expertId, file.rating); + } + + for (const file of claimFiles) { + const expertId = file?.damageExpertReply?.actorDetail?.actorId; + processRatings(expertId, file.rating); + } + + const allExperts = allExpertsRaw.map((expert) => { + const expertIdStr = expert._id.toString(); + + const totalRatings = expertTotalRatingsMap[expertIdStr] || []; + const overallAverageRating = totalRatings.length + ? parseFloat( + ( + totalRatings.reduce((a, b) => a + b, 0) / totalRatings.length + ).toFixed(2), + ) + : null; + + const averageRatingsByCategory: Record = {}; + const ratingsByCat = expertRatingsByCategoryMap[expertIdStr]; + if (ratingsByCat) { + for (const [category, values] of Object.entries(ratingsByCat)) { + const sum = values.reduce((a, b) => a + b, 0); + const average = parseFloat((sum / values.length).toFixed(2)); + averageRatingsByCategory[category] = average; + } + } + + return { + _id: expert._id, + fullName: `${expert.firstName} ${expert.lastName}`, + role: expert.role, + type: expert.userType, + requestStats: expert.requestStats, + createdAt: expert.createdAt, + overallAverageRating, + averageRatingsByCategory, + }; + }); + + const start = (currentPage - 1) * countPerPage; + const paginated = allExperts.slice(start, start + countPerPage); + + return { + total: allExperts.length, + page: currentPage, + countPerPage, + experts: paginated, + }; + } + + async getAllFilesByExpertAndRole(expertId: string, role: "claim" | "blame") { + const expertObjectId = new Types.ObjectId(expertId); + + const model = + role === "claim" + ? this.claimRequestManagementModel + : this.requestManagementModel; + const expertCollection = role === "claim" ? "damage-expert" : "expert"; + + return await model.aggregate([ + { $match: { "actorLocked.actorId": expertObjectId } }, + { + $lookup: { + from: expertCollection, + localField: "actorLocked.actorId", + foreignField: "_id", + as: "expertInfo", + }, + }, + { $unwind: { path: "$expertInfo", preserveNullAndEmptyArrays: true } }, + + { + $addFields: { + averageRating: { + $cond: [ + { $ne: ["$rating", null] }, + { + $avg: [ + "$rating.collisionMethodAccuracy", + "$rating.evaluationTimeliness", + "$rating.accidentCauseAccuracy", + "$rating.guiltyVehicleIdentification", + ], + }, + null, + ], + }, + }, + }, + + { + $project: { + requestNumber: 1, + userClientKey: 1, + userId: 1, + fullName: 1, + carDetail: 1, + claimStatus: 1, + steps: 1, + currentStep: 1, + rating: 1, + averageRating: 1, + expertInfo: { + _id: 1, + fullName: { + $concat: ["$expertInfo.firstName", " ", "$expertInfo.lastName"], + }, + requestStats: 1, + userType: 1, + createdAt: 1, + }, + }, + }, + ]); + } + + async rateExpertOnFile( + requestId: string, + rating: FileRating, + role: "claim" | "blame", + ) { + const _id = new Types.ObjectId(requestId); + + // Validate each rating factor + const fields = Object.entries(rating); + for (const [key, value] of fields) { + if (typeof value !== "number" || value < 0 || value > 5) { + throw new BadRequestException( + `${key} must be a number between 0 and 5`, + ); + } + } + + if (role === "claim") { + const updated = await this.claimRequestManagementModel.findByIdAndUpdate( + _id, + { $set: { rating } }, + { new: true }, + ); + + if (!updated) { + throw new NotFoundException("Claim file not found"); + } + + return { + message: "Claim expert rated successfully", + updatedRating: updated.rating, + requestId: updated._id, + }; + } + + if (role === "blame") { + const updated = await this.requestManagementModel.findByIdAndUpdate( + _id, + { $set: { rating } }, + { new: true }, + ); + + if (!updated) { + throw new NotFoundException("Blame file not found"); + } + + return { + message: "Blame expert rated successfully", + updatedRating: updated.rating, + requestId: updated._id, + }; + } + + throw new BadRequestException("Invalid role"); + } + + async retrieveAllFilesOfClient(insurerId: string) { + const id = new Types.ObjectId(insurerId); + + const blameFilesRaw = await this.requestManagementModel + .find({ + "firstPartyDetails.firstPartyClient.clientId": id, + blameStatus: { + $nin: ["PendingForFirstParty", "PendingForSecondParty"], + }, + }) + .lean(); + + const claimFilesRaw = await this.claimRequestManagementModel + .find({ + userClientKey: id, + }) + .lean(); + + const populatedBlameFiles = await Promise.all( + blameFilesRaw.map((file) => this.populateBlameFileLinks(file)), + ); + const populatedClaimFiles = await Promise.all( + claimFilesRaw.map((file) => this.populateClaimFileLinks(file)), + ); + + return { blameFiles: populatedBlameFiles, claimFiles: populatedClaimFiles }; + } + + private async populateBlameFileLinks(blameFile: any): Promise { + if (!blameFile) return blameFile; + + // --- FIX: Consistently use findById --- + if (blameFile.firstPartyDetails?.firstPartyFile?.firstPartyVideoId) { + const videoDoc = await this.blameVideoDbService.findById( + blameFile.firstPartyDetails.firstPartyFile.firstPartyVideoId.toString(), + ); + if (videoDoc) + blameFile.firstPartyDetails.firstPartyFile.firstPartyVideoId = + buildFileLink(videoDoc.path); + } + if (Array.isArray(blameFile.firstPartyDetails?.firstPartyFile?.voices)) { + blameFile.firstPartyDetails.firstPartyFile.voices = + await this.populateIdArray( + this.blameVoiceDbService, + blameFile.firstPartyDetails.firstPartyFile.voices, + ); + } + if (Array.isArray(blameFile.secondPartyDetails?.secondPartyFiles?.voices)) { + blameFile.secondPartyDetails.secondPartyFiles.voices = + await this.populateIdArray( + this.blameVoiceDbService, + blameFile.secondPartyDetails.secondPartyFiles.voices, + ); + } + + if (blameFile.expertResendReply) { + await this.populatePartyReplyLinks( + blameFile.expertResendReply.firstParty, + ); + await this.populatePartyReplyLinks( + blameFile.expertResendReply.secondParty, + ); + } + + const finalReply = + blameFile.expertSubmitReplyFinal || blameFile.expertSubmitReply; + if (finalReply) { + await this.populateSignatureLink(finalReply.firstPartyComment); + await this.populateSignatureLink(finalReply.secondPartyComment); + } + + return blameFile; + } + + private async populateClaimFileLinks(claimFile: any): Promise { + if (!claimFile) return claimFile; + + if (claimFile.blameFile) { + claimFile.blameFile = await this.populateBlameFileLinks( + claimFile.blameFile, + ); + } + + // --- FIX: Consistently use findById --- + if (claimFile.videoCaptureId) { + const videoDoc = await this.claimVideoCaptureDbService.findById( + claimFile.videoCaptureId._id.toString(), + ); + if (videoDoc) claimFile.videoCaptureId = buildFileLink(videoDoc.path); + } + + return claimFile; + } + + private async populatePartyReplyLinks(partyReply: any) { + if (!partyReply) return; + // --- FIX: Consistently use findById --- + if (partyReply.voice) { + const voiceDoc = await this.blameVoiceDbService.findById( + partyReply.voice.toString(), + ); + if (voiceDoc) partyReply.voice = buildFileLink(voiceDoc.path); + } + if (partyReply.documents) { + for (const docType in partyReply.documents) { + const docId = partyReply.documents[docType]; + if (docId) { + const doc = await this.blameDocumentDbService.findById( + docId.toString(), + ); + if (doc) partyReply.documents[docType] = buildFileLink(doc.path); + } + } + } + } + + private async populateSignatureLink(comment: any) { + if (comment?.signDetail?.fileId) { + // --- FIX: Consistently use findById --- + const signDoc = await this.userSignDbService.findById( + comment.signDetail.fileId.toString(), + ); + if (signDoc) + (comment.signDetail as any).fileUrl = buildFileLink(signDoc.path); + } + } + + private async populateIdArray(dbService: any, ids: any[]): Promise { + return Promise.all( + ids.map(async (id) => { + if (!id) return id; + + if (typeof id === "object" && id.path) { + return buildFileLink(id.path); + } + + const idString = id.toString(); + if (idString.includes("[object Object]")) { + this.logger.warn( + "Invalid object found in ID array, skipping population.", + id, + ); + return id; + } + + try { + // --- FIX: Consistently use findById --- + const doc = await dbService.findById(idString); + return doc ? buildFileLink(doc.path) : id; + } catch (error) { + this.logger.error(`Failed to populate ID: ${idString}`, error); + return id; + } + }), + ); + } + + async addBranch(clientKey: string, branchDto: CreateBranchDto) { + const clientId = new Types.ObjectId(clientKey); + + const existingBranch = await this.branchDbService.findOne({ + clientKey: clientId, + code: branchDto.code, + }); + + if (existingBranch) { + throw new ConflictException( + `A branch with code '${branchDto.code}' already exists for this client.`, + ); + } + + const newBranch = await this.branchDbService.create({ + ...branchDto, + clientKey: clientId, + }); + + return newBranch; + } +} diff --git a/src/expert-insurer/panel.enum.ts b/src/expert-insurer/panel.enum.ts new file mode 100644 index 0000000..c3119eb --- /dev/null +++ b/src/expert-insurer/panel.enum.ts @@ -0,0 +1,4 @@ +export enum PanelModels { + BLAME = "blame", + CLAIM = "claim", +} diff --git a/src/helpers/urlCreator.ts b/src/helpers/urlCreator.ts new file mode 100644 index 0000000..510e04e --- /dev/null +++ b/src/helpers/urlCreator.ts @@ -0,0 +1,9 @@ +export function buildFileLink(path: string): string { + const baseUrl = process.env.URL; + if(process.env.NODE_ENV === 'local'){ + return baseUrl + "/" + path; + }else{ + return baseUrl + "/api/" + path; + } + +} diff --git a/src/interceptor/logging.interceptors.ts b/src/interceptor/logging.interceptors.ts new file mode 100644 index 0000000..86f429e --- /dev/null +++ b/src/interceptor/logging.interceptors.ts @@ -0,0 +1,20 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from "@nestjs/common"; +import { Observable } from "rxjs"; +import { tap } from "rxjs/operators"; + +@Injectable() +export class LoggingInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + console.log("Before..."); + + const now = Date.now(); + return next + .handle() + .pipe(tap(() => console.log(`After... ${Date.now() - now}ms`))); + } +} diff --git a/src/lookups/entities/db-service/lookup.db.service.ts b/src/lookups/entities/db-service/lookup.db.service.ts new file mode 100644 index 0000000..3b5e80c --- /dev/null +++ b/src/lookups/entities/db-service/lookup.db.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, UpdateQuery } from "mongoose"; +import { LookupModel } from "../schema/lookup.schema"; + +@Injectable() +export class LookupDbService { + constructor( + @InjectModel(LookupModel.name) + private readonly lookupModel: Model, + ) {} + + async create(lookupData: Partial): Promise { + return await this.lookupModel.create(lookupData); + } + + async findOne( + filter: FilterQuery, + ): Promise { + return await this.lookupModel.findOne(filter).lean(); + } + + async findOneAndUpdate( + filter: FilterQuery, + update: UpdateQuery, + ): Promise { + return await this.lookupModel.findOneAndUpdate(filter, update, { + new: true, + upsert: true, + }); + } + + async findById(id: string): Promise { + return await this.lookupModel.findById(id).lean(); + } +} + diff --git a/src/lookups/entities/schema/lookup.schema.ts b/src/lookups/entities/schema/lookup.schema.ts new file mode 100644 index 0000000..75e3db1 --- /dev/null +++ b/src/lookups/entities/schema/lookup.schema.ts @@ -0,0 +1,32 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Document } from "mongoose"; + +export type LookupDocument = LookupModel & Document; + +@Schema({ versionKey: false, collection: "lookups", timestamps: true }) +export class LookupModel { + @Prop({ required: true }) + url: string; + + @Prop({ required: true }) + authenticationToken: string; + + @Prop({ required: true }) + name: string; + + @Prop({ type: Object, required: true }) + response: any; + + @Prop() + createdAt: Date; + + @Prop() + updatedAt: Date; +} + +export const LookupSchema = SchemaFactory.createForClass(LookupModel); + + + + + diff --git a/src/lookups/lookups.controller.ts b/src/lookups/lookups.controller.ts new file mode 100644 index 0000000..02adfd1 --- /dev/null +++ b/src/lookups/lookups.controller.ts @@ -0,0 +1,99 @@ +import { Controller, Get } from "@nestjs/common"; +import { ApiOkResponse, ApiTags } from "@nestjs/swagger"; +import { LookupsService } from "./lookups.service"; + +@ApiTags("lookups") +@Controller("lookups") +export class LookupsController { + constructor(private readonly lookupsService: LookupsService) {} + + @Get("accident-causes") + @ApiOkResponse({ + description: "Returns accident causes lookup data", + schema: { + type: "array", + items: { + type: "object", + properties: { + Caption: { type: "string" }, + Id: { type: "number" }, + IsActive: { type: "number" }, + }, + }, + }, + }) + async getAccidentCauses() { + return await this.lookupsService.getAccidentCauses(); + } + + @Get("accident-report-type") + @ApiOkResponse({ + description: "Returns accident report type lookup data", + schema: { + type: "array", + items: { + type: "object", + }, + }, + }) + async getAccidentReportType() { + return await this.lookupsService.getAccidentReportType(); + } + + @Get("vehicle-use-types") + @ApiOkResponse({ + description: "Returns vehicle use types lookup data", + schema: { + type: "array", + items: { + type: "object", + }, + }, + }) + async getVehicleUseTypes() { + return await this.lookupsService.getVehicleUseTypes(); + } + + @Get("dmg-pay-method") + @ApiOkResponse({ + description: "Returns damage pay method lookup data", + schema: { + type: "array", + items: { + type: "object", + }, + }, + }) + async getDmgPayMethod() { + return await this.lookupsService.getDmgPayMethod(); + } + + @Get("driving-licence-types") + @ApiOkResponse({ + description: "Returns driving licence types lookup data", + schema: { + type: "array", + items: { + type: "object", + }, + }, + }) + async getDrivingLicenceTypes() { + return await this.lookupsService.getDrivingLicenceTypes(); + } + + @Get("accident-culprit-type") + @ApiOkResponse({ + description: "Returns accident culprit type lookup data", + schema: { + type: "array", + items: { + type: "object", + }, + }, + }) + async getAccidentCulpritType() { + return await this.lookupsService.getAccidentCulpritType(); + } +} + diff --git a/src/lookups/lookups.module.ts b/src/lookups/lookups.module.ts new file mode 100644 index 0000000..0b60716 --- /dev/null +++ b/src/lookups/lookups.module.ts @@ -0,0 +1,19 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { LookupDbService } from "./entities/db-service/lookup.db.service"; +import { LookupModel, LookupSchema } from "./entities/schema/lookup.schema"; +import { LookupsController } from "./lookups.controller"; +import { LookupsService } from "./lookups.service"; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { name: LookupModel.name, schema: LookupSchema }, + ]), + ], + controllers: [LookupsController], + providers: [LookupsService, LookupDbService], + exports: [LookupsService, LookupDbService], +}) +export class LookupsModule {} + diff --git a/src/lookups/lookups.service.ts b/src/lookups/lookups.service.ts new file mode 100644 index 0000000..cadfea2 --- /dev/null +++ b/src/lookups/lookups.service.ts @@ -0,0 +1,401 @@ +import { + BadGatewayException, + Injectable, + Logger, +} from "@nestjs/common"; +import axios from "axios"; +import { LookupDbService } from "./entities/db-service/lookup.db.service"; + +@Injectable() +export class LookupsService { + private readonly logger = new Logger(LookupsService.name); + private readonly GET_APP_TOKEN_URL = + "https://insapm.ir/bimeapimanager/api/EITAuthentication/GetAppToken"; + private readonly LOGIN_URL = + "https://insapm.ir/bimeapimanager/api/EITAuthentication/Login"; + private readonly BASE_API_URL = + "https://apimanager.iraneit.com/BimeApiManager/api/BimeApi/v2.0/car"; + private readonly BASE_INFO_PATH = "base-info"; + private readonly CODE_LIST_PATH = "code-list"; + + // Authentication credentials + private readonly APP_NAME = "fanhab"; + private readonly SECRET = "5Fa@N#A2B"; + private readonly USERNAME = "fanhabUser"; + private readonly PASSWORD = "Fan#@2U$3er"; + + // API headers + private readonly CORP_ID = "3539"; + private readonly CONTRACT_ID = "263"; + private readonly LOCATION = "100"; + + constructor( + private readonly lookupDbService: LookupDbService, + ) {} + + /** + * Step 1: Get appToken from GetAppToken API + * The token is returned in the response header 'appToken' + */ + private async getAppToken(): Promise { + try { + const requestHeaders: any = { + appname: this.APP_NAME, + secret: this.SECRET, + "Content-Length": "0", + }; + // Remove Content-Type header when Content-Length is 0 + delete requestHeaders["Content-Type"]; + + const response = await axios({ + method: "POST", + url: this.GET_APP_TOKEN_URL, + data: "", // Empty string body + headers: requestHeaders, + transformRequest: [ + (data, headers) => { + // Remove Content-Type header completely for empty body + if (headers) { + delete headers["Content-Type"]; + delete headers["content-type"]; + } + return data; + }, + ], + }); + + // Axios lowercases headers, so check both cases + const appToken = + response.headers.apptoken || + response.headers.appToken || + response.headers["apptoken"] || + response.headers["appToken"]; + if (!appToken) { + this.logger.error("Failed to get appToken from response headers"); + this.logger.error("Response status:", response.status); + this.logger.error("Response headers:", JSON.stringify(response.headers, null, 2)); + this.logger.error("Available header keys:", Object.keys(response.headers)); + this.logger.error("Response data:", response.data); + + // Extract error message from response if available + const errorMessage = response.data?.Message || + response.data?.message || + response.data || + "Failed to get appToken from response headers"; + + throw new BadGatewayException(errorMessage); + } + + this.logger.log(`Successfully obtained appToken: ${appToken}`); + return appToken; + } catch (error) { + this.logger.error("Failed to get appToken"); + if (axios.isAxiosError(error)) { + this.logger.error("Error status:", error.response?.status); + this.logger.error("Error status text:", error.response?.statusText); + this.logger.error("Error response data:", error.response?.data); + this.logger.error("Error response headers:", error.response?.headers); + this.logger.error("Request URL:", error.config?.url); + this.logger.error("Request headers:", error.config?.headers); + + // Extract error message from response + const errorMessage = error.response?.data?.Message || + error.response?.data?.message || + error.response?.data || + error.message || + "Failed to get appToken from external API"; + + throw new BadGatewayException(errorMessage); + } else { + this.logger.error("Error details:", error); + throw new BadGatewayException("Failed to get appToken from external API"); + } + } + } + + /** + * Step 2: Login to get authenticationToken + * The token is returned in the response body field 'authenticationtoken' + */ + private async login(appToken: string): Promise { + try { + this.logger.log(`Attempting login with appToken: ${appToken}`); + this.logger.log(`Login URL: ${this.LOGIN_URL}`); + this.logger.log(`Username: ${this.USERNAME}`); + + const requestHeaders: any = { + appToken: appToken, + userName: this.USERNAME, + password: this.PASSWORD, + "Content-Length": "0", + }; + // Remove Content-Type header when Content-Length is 0 + // Axios adds it automatically, but server doesn't expect it for empty body + delete requestHeaders["Content-Type"]; + + this.logger.log("Request headers (without password):", { + appToken: appToken, + userName: this.USERNAME, + password: "***", + "Content-Length": "0", + "Content-Type": "removed", + }); + + const response = await axios({ + method: "POST", + url: this.LOGIN_URL, + data: "", // Empty string body + headers: requestHeaders, + transformRequest: [ + (data, headers) => { + // Remove Content-Type header completely for empty body + if (headers) { + delete headers["Content-Type"]; + delete headers["content-type"]; + } + return data; + }, + ], + }); + + this.logger.log("Login response status:", response.status); + this.logger.log("Login response data:", JSON.stringify(response.data, null, 2)); + this.logger.log("Login response headers:", JSON.stringify(response.headers, null, 2)); + + // Check response headers first (authenticationToken is in headers, not body) + const authenticationToken = + response.headers.authenticationtoken || + response.headers.authenticationToken || + response.headers["authenticationtoken"] || + response.headers["authenticationToken"] || + // Fallback to body if not in headers + response.data?.authenticationtoken || + response.data?.authenticationToken || + response.data?.authentication_token; + + this.logger.log(`Extracted authenticationToken: ${authenticationToken ? 'Found' : 'Not found'}`); + + if (!authenticationToken) { + this.logger.error("Failed to get authenticationToken from response"); + this.logger.error("Response status:", response.status); + this.logger.error("Response headers:", JSON.stringify(response.headers, null, 2)); + this.logger.error("Response headers keys:", Object.keys(response.headers || {})); + this.logger.error("Response data:", JSON.stringify(response.data, null, 2)); + this.logger.error("Response data keys:", Object.keys(response.data || {})); + + // Extract error message from response if available + const errorMessage = response.data?.Message || + response.data?.message || + "Failed to get authenticationToken from response"; + + throw new BadGatewayException(errorMessage); + } + + this.logger.log(`Successfully obtained authenticationToken: ${authenticationToken}`); + return authenticationToken; + } catch (error) { + this.logger.error("Failed to login"); + this.logger.error("AppToken used:", appToken); + this.logger.error("Login URL:", this.LOGIN_URL); + this.logger.error("Username:", this.USERNAME); + + if (axios.isAxiosError(error)) { + this.logger.error("Error status:", error.response?.status); + this.logger.error("Error status text:", error.response?.statusText); + this.logger.error("Error response data:", JSON.stringify(error.response?.data, null, 2)); + this.logger.error("Error response headers:", JSON.stringify(error.response?.headers, null, 2)); + this.logger.error("Request URL:", error.config?.url); + this.logger.error("Request method:", error.config?.method); + this.logger.error("Request headers:", JSON.stringify({ + ...error.config?.headers, + password: "***", + }, null, 2)); + + // Extract error message from response + const errorMessage = error.response?.data?.Message || + error.response?.data?.message || + error.response?.data || + error.message || + "Failed to login to external API"; + + throw new BadGatewayException(errorMessage); + } else { + this.logger.error("Error details:", error); + throw new BadGatewayException("Failed to login to external API"); + } + } + } + + /** + * Step 3: Call the lookup API with authenticationToken + */ + private async callLookupApi( + lookupName: string, + authenticationToken: string, + pathType: "base-info" | "code-list" = "base-info", + ): Promise { + const url = `${this.BASE_API_URL}/${pathType}/${lookupName}`; + try { + const response = await axios.get(url, { + headers: { + authenticationToken: authenticationToken, + CorpId: this.CORP_ID, + ContractId: this.CONTRACT_ID, + Location: this.LOCATION, + "Content-Type": "application/json", + }, + }); + + this.logger.log(`Successfully fetched data for lookup: ${lookupName}`); + return response.data; + } catch (error) { + this.logger.error(`Failed to fetch lookup data for ${lookupName}`); + this.logger.error("URL:", url); + this.logger.error("AuthenticationToken used:", authenticationToken); + + if (axios.isAxiosError(error)) { + this.logger.error("Error status:", error.response?.status); + this.logger.error("Error status text:", error.response?.statusText); + this.logger.error("Error response data:", JSON.stringify(error.response?.data, null, 2)); + this.logger.error("Error response headers:", JSON.stringify(error.response?.headers, null, 2)); + this.logger.error("Request URL:", error.config?.url); + this.logger.error("Request headers:", JSON.stringify(error.config?.headers, null, 2)); + // Extract error message from response + const errorMessage = error.response?.data?.Message || + error.response?.data?.message || + error.response?.data || + error.message || + `Failed to fetch lookup data for ${lookupName}`; + + throw new BadGatewayException(errorMessage); + } else { + this.logger.error("Error details:", error); + throw new BadGatewayException( + `Failed to fetch lookup data for ${lookupName}`, + ); + } + } + } + + /** + * Complete authentication flow and fetch lookup data + */ + private async fetchLookupDataFromExternalApi( + lookupName: string, + pathType: "base-info" | "code-list" = "base-info", + ): Promise<{ authenticationToken: string; response: any; url: string }> { + // Step 1: Get appToken + const appToken = await this.getAppToken(); + + // Step 2: Login to get authenticationToken + const authenticationToken = await this.login(appToken); + + // Step 3: Call the lookup API + const url = `${this.BASE_API_URL}/${pathType}/${lookupName}`; + const response = await this.callLookupApi( + lookupName, + authenticationToken, + pathType, + ); + + return { authenticationToken, response, url }; + } + + /** + * Check if lookup data needs to be updated (once a month) + */ + private shouldUpdateLookup(updatedAt: Date | null): boolean { + if (!updatedAt) { + return true; // Never updated, need to fetch + } + + const oneMonthAgo = new Date(); + oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); + + return updatedAt < oneMonthAgo; + } + + /** + * Get lookup data by name + * Returns cached data if available and not expired, otherwise fetches from external API + */ + async getLookup( + lookupName: string, + pathType: "base-info" | "code-list" = "base-info", + ): Promise { + // Check if we have cached data + const existingLookup = await this.lookupDbService.findOne({ + name: lookupName, + }); + + if (existingLookup && !this.shouldUpdateLookup(existingLookup.updatedAt)) { + this.logger.log( + `Returning cached data for lookup: ${lookupName} (last updated: ${existingLookup.updatedAt})`, + ); + return existingLookup.response; + } + + // Need to fetch from external API + this.logger.log(`Fetching fresh data for lookup: ${lookupName}`); + const { authenticationToken, response, url } = + await this.fetchLookupDataFromExternalApi(lookupName, pathType); + + // Save or update in database + await this.lookupDbService.findOneAndUpdate( + { name: lookupName }, + { + url: url, + authenticationToken: authenticationToken, + name: lookupName, + response: response, + updatedAt: new Date(), + }, + ); + + this.logger.log(`Successfully saved/updated lookup data for: ${lookupName}`); + return response; + } + + /** + * Get accident-causes lookup data + */ + async getAccidentCauses(): Promise { + return await this.getLookup("accident-causes", "base-info"); + } + + /** + * Get accident-report-type lookup data + */ + async getAccidentReportType(): Promise { + return await this.getLookup("accident-report-type", "code-list"); + } + + /** + * Get vehicle-use-types lookup data + */ + async getVehicleUseTypes(): Promise { + return await this.getLookup("vehicle-use-types", "base-info"); + } + + /** + * Get dmg-pay-method lookup data + */ + async getDmgPayMethod(): Promise { + return await this.getLookup("dmg-pay-method", "code-list"); + } + + /** + * Get driving-licence-types lookup data + */ + async getDrivingLicenceTypes(): Promise { + return await this.getLookup("driving-licence-types", "base-info"); + } + + /** + * Get accident-culprit-type lookup data + */ + async getAccidentCulpritType(): Promise { + return await this.getLookup("accident-culprit-type", "code-list"); + } +} + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..a796ad0 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,45 @@ +import { NestFactory } from "@nestjs/core"; +import { NestExpressApplication } from "@nestjs/platform-express"; +import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; +import * as basicAuth from "express-basic-auth"; +import { AppModule } from "./app.module"; + +async function bootstrap() { + const app = await NestFactory.create(AppModule, { + cors: true, + }); + + app.enableCors(); + + app.enableCors({ + origin: "*", + methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS", + allowedHeaders: "Content-Type, Authorization", + }); + + app.use( + ["/docs", "/docs-json", "/swagger"], + basicAuth({ + challenge: true, + users: { + [process.env.SWAGGER_USER]: process.env.SWAGGER_PASSWORD, + }, + }), + ); + + const config = new DocumentBuilder() + .setTitle("yara724-backend") + .setVersion("1.0.0") + .addServer(process.env.BASE_URL + "/api") + .addServer(process.env.URL) + .addServer("http://192.168.20.249:9001") + .addServer("http://192.168.20.170:9001") + .addServer("http://localhost:9001") + .addBearerAuth() + .build(); + const docs = SwaggerModule.createDocument(app, config); + SwaggerModule.setup("/docs", app, docs); + await app.listen(process.env.PORT); +} + +bootstrap(); diff --git a/src/plates/dto/plate.dto.ts b/src/plates/dto/plate.dto.ts new file mode 100644 index 0000000..8672461 --- /dev/null +++ b/src/plates/dto/plate.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class PlatesDto { + @ApiProperty({ type: "number" }) + leftDigits: number; + + @ApiProperty({ type: "string" }) + centerAlphabet: string; + + @ApiProperty({ type: "number" }) + centerDigits: number; + + @ApiProperty({ type: "number" }) + ir: number; +} +export class PlatesDtoRs extends PlatesDto { + constructor(newPlate: PlatesDto) { + super(); + this.leftDigits = newPlate.leftDigits; + this.centerAlphabet = newPlate.centerAlphabet; + this.centerDigits = newPlate.centerDigits; + this.ir = newPlate.ir; + } +} diff --git a/src/plates/entites/db-service/plate.db.service.ts b/src/plates/entites/db-service/plate.db.service.ts new file mode 100644 index 0000000..d022bb3 --- /dev/null +++ b/src/plates/entites/db-service/plate.db.service.ts @@ -0,0 +1,26 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types } from "mongoose"; +import { PlatesModel } from "../schema/plate.schema"; + +export class PlateDbService { + constructor( + @InjectModel(PlatesModel.name) + private readonly plateModel: Model, + ) {} + + async create(plate: PlatesModel): Promise { + return await this.plateModel.create(plate); + } + + async findOne(plate: PlatesModel): Promise { + return await this.plateModel.findOne(plate); + } + + async findAndRemove(id: Types.ObjectId): Promise { + return await this.plateModel.findOneAndUpdate( + { _id: id }, + { isDelete: true }, + { upsert: true }, + ); + } +} diff --git a/src/plates/entites/schema/plate.schema.ts b/src/plates/entites/schema/plate.schema.ts new file mode 100644 index 0000000..cbc8bb8 --- /dev/null +++ b/src/plates/entites/schema/plate.schema.ts @@ -0,0 +1,24 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; + +@Schema({ collection: "plates", versionKey: false }) +export class PlatesModel { + @Prop({ required: true, unique: false }) + userId: string; + + @Prop({ type: "number", maxlength: 2 }) + leftDigits: number; + + @Prop({ type: "string", maxlength: 1 }) + centerAlphabet: string; + + @Prop({ type: "number", maxlength: 3 }) + centerDigits: number; + + @Prop({ type: "number", maxlength: 2 }) + ir: number; + + @Prop() + nationalCodeOfInsurer: string; +} + +export const PlatesSchema = SchemaFactory.createForClass(PlatesModel); diff --git a/src/plates/interface/plate.interface.ts b/src/plates/interface/plate.interface.ts new file mode 100644 index 0000000..f50ef87 --- /dev/null +++ b/src/plates/interface/plate.interface.ts @@ -0,0 +1,8 @@ +export interface PlateType { + nationalCodeOfInsurer: string; + userId: string; + leftDigits: number; + centerAlphabet: string; + centerDigits: number; + ir: number; +} diff --git a/src/plates/plates.module.ts b/src/plates/plates.module.ts new file mode 100644 index 0000000..4da9f75 --- /dev/null +++ b/src/plates/plates.module.ts @@ -0,0 +1,22 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { ClientModule } from "src/client/client.module"; +import { PlateDbService } from "src/plates/entites/db-service/plate.db.service"; +import { SandHubModule } from "src/sand-hub/sand-hub.module"; +import { UsersModule } from "src/users/users.module"; +import { PlatesModel, PlatesSchema } from "./entites/schema/plate.schema"; +import { PlatesService } from "./plates.service"; + +@Module({ + imports: [ + UsersModule, + SandHubModule, + ClientModule, + MongooseModule.forFeature([ + { name: PlatesModel.name, schema: PlatesSchema }, + ]), + ], + providers: [PlatesService, PlateDbService], + exports: [PlatesService, PlateDbService], +}) +export class PlatesModule {} diff --git a/src/plates/plates.service.ts b/src/plates/plates.service.ts new file mode 100644 index 0000000..e6ffd11 --- /dev/null +++ b/src/plates/plates.service.ts @@ -0,0 +1,71 @@ +import { + HttpException, + HttpStatus, + Injectable, + NotFoundException, +} from "@nestjs/common"; +import { Types } from "mongoose"; +import { PlateDbService } from "src/plates/entites/db-service/plate.db.service"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; + +@Injectable() +export class PlatesService { + constructor( + private readonly plateDbService: PlateDbService, + ) {} + + async addPlate(currentUser, body: AddPlateDto) { + const { plate, nationalCodeOfInsurer } = body; + + const duplicate = await this.plateDbService.findOne({ + userId: currentUser, + leftDigits: plate.leftDigits, + centerAlphabet: plate.centerAlphabet, + centerDigits: plate.centerDigits, + ir: plate.ir, + nationalCodeOfInsurer, + }); + + if (duplicate) + throw new HttpException("duplicate plate", HttpStatus.CONFLICT); + + const newPlateData = await this.plateDbService.create({ + ...plate, + userId: currentUser, + nationalCodeOfInsurer: body.nationalCodeOfInsurer, + }); + + return { + message: "Plate successfully added", + plate: newPlateData, + }; + } + + private async checkUniquePlate(plate): Promise { + const { + userId, + leftDigits, + centerAlphabet, + centerDigits, + ir, + nationalCodeOfInsurer, + } = plate; + + const found = await this.plateDbService.findOne({ + leftDigits, + centerAlphabet, + centerDigits, + userId, + nationalCodeOfInsurer, + ir, + }); + + return !found; + } + + async deletePlate(id: Types.ObjectId) { + const deleted = await this.plateDbService.findAndRemove(id); + if (!deleted) throw new NotFoundException("Plate not found"); + return { message: "Plate deleted successfully" }; + } +} diff --git a/src/profile/dto/actor/profile.dto.ts b/src/profile/dto/actor/profile.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/profile/dto/user/AddPlateDto.ts b/src/profile/dto/user/AddPlateDto.ts new file mode 100644 index 0000000..bd090dd --- /dev/null +++ b/src/profile/dto/user/AddPlateDto.ts @@ -0,0 +1,51 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { PlatesDto } from "src/plates/dto/plate.dto"; +import { Plates } from "src/Types&Enums/plate.interface"; + +export class AddPlateDto { + plateId: string; + @ApiProperty({ type: String, required: true }) + nationalCodeOfInsurer: string; + + @ApiProperty({ type: String, required: true }) + nationalCodeOfDriver: string; + + @ApiProperty({ type: String, required: true }) + insurerLicense: string; + + @ApiProperty({ type: String, required: true }) + driverLicense: string; + + @ApiProperty({ type: PlatesDto, required: true }) + plate: Plates; + + @ApiProperty({ type: Boolean, required: true }) + driverIsInsurer: boolean; + + @ApiProperty({ type: Boolean, required: true, default: false }) + isNewCar: boolean; + + @ApiProperty({ type: Boolean, required: true }) + userNoCertificate: boolean; + + @ApiProperty({ + type: Number, + required: true, + }) + insurerBirthday: number; + @ApiProperty({ + type: String, + required: false, + }) + driverBirthday: string | null; +} + +export class AddPlateProfileDto { + plateId: string; + + @ApiProperty({ type: PlatesDto, required: true }) + plate: Plates; + + @ApiProperty({ type: String, required: true }) + nationalCodeOfInsurer: string; +} diff --git a/src/profile/dto/user/profile.dto.ts b/src/profile/dto/user/profile.dto.ts new file mode 100644 index 0000000..58ad24d --- /dev/null +++ b/src/profile/dto/user/profile.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { UserModel } from "src/users/entities/schema/user.schema"; + +export class ProfileUserRq { + @ApiProperty({ required: true }) + id: string; +} + +export class ProfileUserRs extends UserModel { + userId: string; + fullName: string; + constructor(profile: UserModel, carDetail) { + super(); + this.userId = profile["_id"]; + this.fullName = profile.fullName; + this.mobile = profile.mobile; + this.plates = profile.plates || null; + this.nationalCode = profile.nationalCode; + this.birthDay = profile.birthDay; + this.gender = profile.gender; + this.plates = profile.plates; + this.city = profile.city; + // this.carDetail + } +} diff --git a/src/profile/dto/user/update-profile.dto.ts b/src/profile/dto/user/update-profile.dto.ts new file mode 100644 index 0000000..240f870 --- /dev/null +++ b/src/profile/dto/user/update-profile.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty, ApiSchema } from "@nestjs/swagger"; +import { IsDateString } from "class-validator"; + +@ApiSchema({ + name: "Update user profile", + description: "Users updates their profile via this API.", +}) +export class UpdateUserProfileDtoRq { + // @ApiProperty({}) + // mobile: string; + @ApiProperty({ + type: "string", + description: "Birth date of the user", + example: "1999-03-05", + nullable: true, + }) + @IsDateString() + birthDay: string; + + @ApiProperty() + gender?: "male" | "female"; + + @ApiProperty() + city: string; + + @ApiProperty() + state: string; + + @ApiProperty() + address: string; +} diff --git a/src/profile/profile.controller.spec.ts b/src/profile/profile.controller.spec.ts new file mode 100644 index 0000000..fdccf9f --- /dev/null +++ b/src/profile/profile.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { ProfileController } from "./profile.controller"; +import { ProfileService } from "./profile.service"; + +describe("ProfileController", () => { + let controller: ProfileController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ProfileController], + providers: [ProfileService], + }).compile(); + + controller = module.get(ProfileController); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/profile/profile.controller.ts b/src/profile/profile.controller.ts new file mode 100644 index 0000000..10c4812 --- /dev/null +++ b/src/profile/profile.controller.ts @@ -0,0 +1,63 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + UseGuards, +} from "@nestjs/common"; +import { ApiBearerAuth, ApiBody, ApiParam, ApiTags } from "@nestjs/swagger"; +import { GlobalGuard } from "src/auth/guards/global.guard"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { PlatesService } from "src/plates/plates.service"; +import { AddPlateDto, AddPlateProfileDto } from "./dto/user/AddPlateDto"; +import { UpdateUserProfileDtoRq } from "./dto/user/update-profile.dto"; +import { ProfileService } from "./profile.service"; + +@Controller("user/profile") +@ApiTags("user") +@ApiBearerAuth() +@Roles() +export class ProfileController { + constructor( + private readonly profileService: ProfileService, + private readonly plateService: PlatesService, + ) {} + + @Get() + @UseGuards(GlobalGuard) + async getProfile(@CurrentUser() user) { + return await this.profileService.getProfileDetail(user.username); + } + + @Patch() + @UseGuards(GlobalGuard) + @ApiBody({ type: UpdateUserProfileDtoRq }) + async updateProfile( + @CurrentUser() user, + @Body() body: UpdateUserProfileDtoRq, + ) { + return await this.profileService.updateProfile(user.username, body); + } + + @Post("plate") + @UseGuards(GlobalGuard) + @ApiBody({ type: AddPlateProfileDto }) + async addPlate(@CurrentUser() user, @Body() body: AddPlateDto) { + console.log( + "🚀 ~ file: profile.controller.ts:50 ~ ProfileController ~ addPlate ~ user:", + user, + ); + return await this.plateService.addPlate(user.sub, body); + } + + @Delete("plate/:id") + @UseGuards(GlobalGuard) + @ApiParam({ name: "id", required: true }) + async deletePlate(@Param("id") id) { + return await this.plateService.deletePlate(id); + } +} diff --git a/src/profile/profile.module.ts b/src/profile/profile.module.ts new file mode 100644 index 0000000..c869ebb --- /dev/null +++ b/src/profile/profile.module.ts @@ -0,0 +1,12 @@ +import { Module } from "@nestjs/common"; +import { PlatesModule } from "src/plates/plates.module"; +import { UsersModule } from "src/users/users.module"; +import { ProfileController } from "./profile.controller"; +import { ProfileService } from "./profile.service"; + +@Module({ + imports: [UsersModule, PlatesModule], + controllers: [ProfileController], + providers: [ProfileService], +}) +export class ProfileModule {} diff --git a/src/profile/profile.service.spec.ts b/src/profile/profile.service.spec.ts new file mode 100644 index 0000000..72ef957 --- /dev/null +++ b/src/profile/profile.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { ProfileService } from "./profile.service"; + +describe("ProfileService", () => { + let service: ProfileService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ProfileService], + }).compile(); + + service = module.get(ProfileService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/profile/profile.service.ts b/src/profile/profile.service.ts new file mode 100644 index 0000000..ff111cb --- /dev/null +++ b/src/profile/profile.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from "@nestjs/common"; +import { UserDbService } from "src/users/entities/db-service/user.db.service"; +import { ProfileUserRs } from "./dto/user/profile.dto"; + +@Injectable() +export class ProfileService { + constructor(private readonly userDbService: UserDbService) {} + + async getProfileDetail(username: string) { + const userDetail = await this.userDbService.findOne({ username }); + return new ProfileUserRs(userDetail, {}); + } + + async updateProfile(username: string, update) { + return await this.userDbService.findOneAndUpdate({ username }, update); + } +} diff --git a/src/reports/dto/reports.dto.ts b/src/reports/dto/reports.dto.ts new file mode 100644 index 0000000..a9e382e --- /dev/null +++ b/src/reports/dto/reports.dto.ts @@ -0,0 +1,51 @@ +export class DamageExpertAllRequestsCountReportDtoRs { + all: number; + UnChecked: number; + CheckedRequest: number; + CheckAgain: number; + WaitingForUserToResend: number; + CloseRequest: number; + WaitingForUserCompleted: number; + InPersonVisit: number; + + constructor(report: any) { + this.all = report.all; + this.UnChecked = report.UnChecked; + this.CheckedRequest = report.CheckedRequest; + this.CheckAgain = report.CheckAgain; + this.WaitingForUserToResend = report.WaitingForUserToResend; + this.CloseRequest = report.CloseRequest; + this.WaitingForUserCompleted = report.WaitingForUserCompleted; + this.InPersonVisit = report.InPersonVisit; + } +} + +export class ExpertAllRequestsCountReportDtoRs { + all: number; + UnChecked: number; + CheckedRequest: number; + CheckAgain: number; + WaitingForUserToResend: number; + CloseRequest: number; + WaitingForUserCompleted: number; + + constructor(report: any) { + this.all = report.all; + this.UnChecked = report.UnChecked; + this.CheckedRequest = report.CheckedRequest; + this.CheckAgain = report.CheckAgain; + this.WaitingForUserToResend = report.WaitingForUserToResend; + this.CloseRequest = report.CloseRequest; + this.WaitingForUserCompleted = report.WaitingForUserCompleted; + } +} + +export class CompanyAllRequestsCountReportDtoRs { + blame: object; + claim: object; + + constructor(blame: object, claim: object) { + this.blame = blame; + this.claim = claim; + } +} diff --git a/src/reports/reports.controller.ts b/src/reports/reports.controller.ts new file mode 100644 index 0000000..592f93c --- /dev/null +++ b/src/reports/reports.controller.ts @@ -0,0 +1,39 @@ +import { Controller, Get, UseGuards } from "@nestjs/common"; +import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; +import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { ClientKey } from "src/decorators/clientKey.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { ReportsService } from "./reports.service"; + +@ApiTags("reports") +@ApiBearerAuth() +@UseGuards(LocalActorAuthGuard, RolesGuard) +@Roles(RoleEnum.DAMAGE_EXPERT, RoleEnum.EXPERT, RoleEnum.COMPANY) +@Controller("reports") +export class ReportsController { + constructor(private readonly reportsService: ReportsService) {} + + @Get("/report/requests") + async getAllRequestsReport(@CurrentUser() actor, @ClientKey() client) { + return await this.reportsService.getAllRequestsReportCount(actor, client); + } + + @Get("/report/perMonthRequests") + async getAllRequestsReportPerMonth( + @CurrentUser() actor, + @ClientKey() client, + ) { + return await this.reportsService.getAllRequestsReportPerMonth( + actor, + client, + ); + } + + @Get("/report/checkedRequests") + async getAllCheckedRequestsCount(@CurrentUser() actor, @ClientKey() client) { + return await this.reportsService.getAllCheckedRequestsCount(actor, client); + } +} diff --git a/src/reports/reports.module.ts b/src/reports/reports.module.ts new file mode 100644 index 0000000..4f4b5db --- /dev/null +++ b/src/reports/reports.module.ts @@ -0,0 +1,12 @@ +import { Module } from "@nestjs/common"; +import { ClaimRequestManagementModule } from "src/claim-request-management/claim-request-management.module"; +import { RequestManagementModule } from "src/request-management/request-management.module"; +import { ReportsController } from "./reports.controller"; +import { ReportsService } from "./reports.service"; + +@Module({ + imports: [RequestManagementModule, ClaimRequestManagementModule], + controllers: [ReportsController], + providers: [ReportsService], +}) +export class ReportsModule {} diff --git a/src/reports/reports.service.ts b/src/reports/reports.service.ts new file mode 100644 index 0000000..decfbb2 --- /dev/null +++ b/src/reports/reports.service.ts @@ -0,0 +1,284 @@ +import { Injectable } from "@nestjs/common"; +import { Types } from "mongoose"; +import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service"; +import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; +import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; +import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; +import { + CompanyAllRequestsCountReportDtoRs, + DamageExpertAllRequestsCountReportDtoRs, + ExpertAllRequestsCountReportDtoRs, +} from "./dto/reports.dto"; + +@Injectable() +export class ReportsService { + constructor( + private readonly requestManagementDbService: RequestManagementDbService, + private readonly claimRequestManagementDbService: ClaimRequestManagementDbService, + ) {} + + async getAllCheckedRequestsCountFn(role: string, client: string) { + const statuses = ["UnChecked", "CheckedRequest"]; + const data: Record = { all: 0 }; + const clientId = new Types.ObjectId(client); + + for (const status of statuses) { + let count = 0; + if (role === "damage_expert") { + count = await this.claimRequestManagementDbService.countByFilter({ + claimStatus: status, + userClientKey: clientId, + }); + } else { + count = await this.requestManagementDbService.countByFilter({ + blameStatus: status, + $or: [ + { "firstPartyDetails.firstPartyClient.clientId": clientId }, + { "secondPartyDetails.secondPartyClient.clientId": clientId }, + ], + }); + } + data[status] = count; + data.all += count; + } + return data; + } + + async getDateFilteredRequestByStatus( + role: string, + client: string, + start: Date, + end: Date, + ) { + const statuses = ["UnChecked", "CheckedRequest", "CheckAgain"]; + const data: Record = {}; + const clientId = new Types.ObjectId(client); + + for (const status of statuses) { + let blameCount = 0; + let claimCount = 0; + + if (role === "expert" || role === "company") { + blameCount = await this.requestManagementDbService.countByFilter({ + blameStatus: status, + createdAt: { $gte: start, $lte: end }, + $or: [ + { "firstPartyDetails.firstPartyClient.clientId": clientId }, + { "secondPartyDetails.secondPartyClient.clientId": clientId }, + ], + }); + } + + if (role === "damage_expert" || role === "company") { + claimCount = await this.claimRequestManagementDbService.countByFilter({ + claimStatus: status, + userClientKey: clientId, + createdAt: { $gte: start, $lte: end }, + }); + } + data[status] = blameCount + claimCount; + } + return data; + } + + async getAllRequestsCountByRole(role: string, client: string) { + if (role === "expert") { + const statuses = Object.values(ReqBlameStatus); + const data: Record = { all: 0 }; + + for (const status of statuses) { + const filter = { + blameStatus: status, + $or: [ + { + "firstPartyDetails.firstPartyClient.clientId": new Types.ObjectId( + client, + ), + }, + // { + // "secondPartyDetails.secondPartyClient.clientId": + // new Types.ObjectId(client), + // }, + ], + }; + + const count = + await this.requestManagementDbService.countByFilter(filter); + data[status] = count; + data.all += count; + } + + return data; + } + + if (role === "damage_expert") { + const statuses = Object.values(ReqClaimStatus); + const data: Record = { all: 0 }; + + for (const status of statuses) { + const filter = { + claimStatus: status, + userClientKey: new Types.ObjectId(client), + }; + + const count = + await this.claimRequestManagementDbService.countByFilter(filter); + data[status] = count; + data.all += count; + } + + return data; + } + + // ✅ Company logic + if (role === "company") { + const blameStatuses = Object.values(ReqBlameStatus); + const claimStatuses = Object.values(ReqClaimStatus); + + const blameData: Record = { all: 0 }; + const claimData: Record = { all: 0 }; + + // Only count files where this company is FIRST party in Blame + for (const status of blameStatuses) { + const filter = { + blameStatus: status, + "firstPartyDetails.firstPartyClient.clientId": new Types.ObjectId( + client, + ), + }; + + const count = + await this.requestManagementDbService.countByFilter(filter); + blameData[status] = count; + blameData.all += count; + } + + // Claims always use `userClientKey` + for (const status of claimStatuses) { + const filter = { + claimStatus: status, + userClientKey: new Types.ObjectId(client), + }; + + const count = + await this.claimRequestManagementDbService.countByFilter(filter); + claimData[status] = count; + claimData.all += count; + } + + return { blameData, claimData }; + } + + return null; + } + + async getAllRequestsReportCount(actor, client) { + if (actor.role === "damage_expert") { + const data = await this.getAllRequestsCountByRole(actor.role, client); + return new DamageExpertAllRequestsCountReportDtoRs(data); + } else if (actor.role === "expert") { + const data = await this.getAllRequestsCountByRole(actor.role, client); + return new ExpertAllRequestsCountReportDtoRs(data); + } else { + // company + const damageExpertData = await this.getAllRequestsCountByRole( + "damage_expert", + client, + ); + const expertData = await this.getAllRequestsCountByRole("expert", client); + return new CompanyAllRequestsCountReportDtoRs( + expertData, + damageExpertData, + ); + } + } + + async getAllRequestsReportPerMonth(actor, client) { + const result = []; + const now = new Date(); + + if (actor.role === "company") { + for (let i = 0; i < 5; i++) { + const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1); + const monthEnd = new Date( + now.getFullYear(), + now.getMonth() - i + 1, + 0, + 23, + 59, + 59, + ); + const label = monthStart.toLocaleDateString("fa-IR", { + month: "long", + year: "numeric", + }); + + const blameData = await this.getDateFilteredRequestByStatus( + "expert", + client, + monthStart, + monthEnd, + ); + const claimData = await this.getDateFilteredRequestByStatus( + "damage_expert", + client, + monthStart, + monthEnd, + ); + + result.unshift({ + stDate: monthStart, + enDate: monthEnd, + faLabel: label, + data: { + blame: blameData, + claim: claimData, + }, + }); + } + return result; + } else { + for (let i = 0; i < 5; i++) { + const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1); + const monthEnd = new Date( + now.getFullYear(), + now.getMonth() - i + 1, + 0, + 23, + 59, + 59, + ); + const label = monthStart.toLocaleDateString("fa-IR", { + month: "long", + year: "numeric", + }); + const data = await this.getDateFilteredRequestByStatus( + actor.role, + client, + monthStart, + monthEnd, + ); + result.unshift({ + stDate: monthStart, + enDate: monthEnd, + faLabel: label, + data, + }); + } + return result; + } + } + + async getAllCheckedRequestsCount(actor, client) { + if (actor.role === "damage_expert" || actor.role === "expert") { + return this.getAllCheckedRequestsCountFn(actor.role, client); + } else { + const blame = await this.getAllCheckedRequestsCountFn("expert", client); + const claim = await this.getAllCheckedRequestsCountFn( + "damage_expert", + client, + ); + return { blame, claim }; + } + } +} diff --git a/src/request-management/dto/create-request-management.dto.ts b/src/request-management/dto/create-request-management.dto.ts new file mode 100644 index 0000000..de346d6 --- /dev/null +++ b/src/request-management/dto/create-request-management.dto.ts @@ -0,0 +1,211 @@ +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { Types } from "mongoose"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum"; +import { + WeatherCondition, + RoadCondition, + LightCondition, +} from "src/Types&Enums/blame-request-management/accident-conditions.enum"; + +export class InitialFormDto { + @ApiProperty({ required: false, default: false }) + expertOpinion?: boolean; + + @ApiProperty({ required: false, default: false }) + imDamaged?: boolean; + + @ApiProperty({ required: true, default: false }) + imGuilty?: boolean; +} + +export class CarBodyFormDto { + @ApiProperty({ required: false, default: false }) + car?: boolean; + + @ApiProperty({ required: false, default: false }) + object?: boolean; +} + +export class CarBodySecondForm { + @ApiProperty({ required: false, default: false }) + imDamaged?: boolean; + + @ApiProperty({ required: true, default: false }) + imGuilty?: boolean; +} + +export class FirstPartyFileDto { + @ApiProperty() + descriptions?: string; + + @ApiProperty() + voices?: string; + + @ApiPropertyOptional({ type: String }) + firstPartyVideoId?: string; +} + +export class DescriptionDto { + @ApiProperty() + desc: string; + + // CAR_BODY specific fields + @ApiPropertyOptional({ + description: "Date of accident (for CAR_BODY type only)", + example: "2025-12-08" + }) + accidentDate?: Date; + + @ApiPropertyOptional({ + description: "Time of accident (for CAR_BODY type only)", + example: "14:30" + }) + accidentTime?: string; + + @ApiPropertyOptional({ + enum: WeatherCondition, + description: "Weather condition at time of accident (for CAR_BODY type only)", + example: WeatherCondition.CLEAR + }) + weatherCondition?: WeatherCondition; + + @ApiPropertyOptional({ + enum: RoadCondition, + description: "Road condition at time of accident (for CAR_BODY type only)", + example: RoadCondition.DRY + }) + roadCondition?: RoadCondition; + + @ApiPropertyOptional({ + enum: LightCondition, + description: "Light condition at time of accident (for CAR_BODY type only)", + example: LightCondition.DAYLIGHT + }) + lightCondition?: LightCondition; +} + +export class FirstPartyDetail { + // @ApiProperty({ type: String }) + firstPartyId?: Types.ObjectId; + // @ApiProperty({ type: String }) + firstPartyPhoneNumber?: string; + // @ApiProperty({ type: String }) + firstPartyClientId?: string; + @ApiProperty({ + type: AddPlateDto, + description: "first add plate and send plate id to this field", + }) + firstPartyPlate?: AddPlateDto; + + @ApiProperty() + firstPartyFile?: FirstPartyFileDto; +} + +export class SecondPartyDetail { + @ApiProperty({ type: String }) + secondPartyId?: Types.ObjectId; + + @ApiProperty({ type: String }) + secondPartyPhoneNumber?: string; + + @ApiProperty({ type: String }) + secondPartyClientId?: string; + + @ApiProperty({ + type: String, + description: "first add plate and send plate id to this field", + }) + secondPartyPlateId?: Types.ObjectId; + + @ApiProperty({ type: String }) + secondPartyVideoId?: string; +} + +export class LocationDto { + @ApiProperty({ type: Number, required: true }) + lat: number; + + @ApiProperty({ type: Number, required: true }) + lon: number; +} + +export class CreateRequestDto { + @ApiProperty({ type: String, required: false }) + type?: "THIRD_PARTY" | "CAR_BODY"; +} + +// export class DocsOfThisFile { +// @ApiProperty() +// firstPartyFile?: FirstPartyFileDto; +// } +export class CreateRequestManagementDto { + @ApiPropertyOptional({ required: false, description: "first step" }) + initialForm?: InitialFormDto; + + @ApiPropertyOptional({ required: false, description: "first step" }) + firstPartyDetails?: FirstPartyDetail; + + // @ApiPropertyOptional({ required: false, description: "second step call" }) + // docsOFirstParty?: DocsOfThisFile; + @ApiPropertyOptional({ required: false }) + secondPartyDetails?: SecondPartyDetail; + + @ApiPropertyOptional({ required: false, description: "third step call" }) + locationForm?: LocationDto; + + @ApiProperty({ type: "string", format: "binary", required: false }) + firstPartyVideoId: Express.Multer.File; +} +// type: "object", +// properties: { +// file: { +// type: "string", +// format: "binary", +// }, +// }, + +export class CreateRequestManagementInitialFormStep { + step?: StepsEnum; + userId?: Types.ObjectId; + nextStep?: StepsEnum; + message?: string; + requestId?: Types.ObjectId; + @ApiPropertyOptional({ required: false, description: "first step" }) + initialForm?: InitialFormDto; + + constructor(userId, reqId, step, nextStep, message?) { + this.requestId = reqId; + this.userId = userId; + this.step = step; + this.nextStep = nextStep; + this.message = message; + } +} + +export class RequestManagementDtoRs { + step?: StepsEnum; + firstPartyUserId?: Types.ObjectId; + secondPartyUserId?: Types.ObjectId | null; + nextStep?: StepsEnum; + message?: string; + requestId?: Types.ObjectId; + claimFile?: Types.ObjectId | null; + constructor( + firstPartyUser, + secondPartyUserId, + step, + nextStep, + message?, + requestId?, + claimFile = null, + ) { + this.requestId = requestId; + this.firstPartyUserId = firstPartyUser; + this.secondPartyUserId = secondPartyUserId; + this.step = step; + this.nextStep = nextStep; + this.message = message; + this.claimFile = claimFile; + } +} diff --git a/src/request-management/dto/expert-accident-fields.dto.ts b/src/request-management/dto/expert-accident-fields.dto.ts new file mode 100644 index 0000000..66ec527 --- /dev/null +++ b/src/request-management/dto/expert-accident-fields.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { AccidentWayIF, AccidentReasonIF } from "src/expert-blame/dto/reply.dto"; + +/** + * DTO for expert to add accident fields to an expert-initiated file + */ +export class ExpertAccidentFieldsDto { + @ApiProperty({ + type: AccidentWayIF, + description: "Accident way information (id and label)", + }) + accidentWay: AccidentWayIF; + + @ApiProperty({ + type: AccidentReasonIF, + description: "Accident reason information (id, label, and fanavaran)", + }) + accidentReason: AccidentReasonIF; + + @ApiProperty({ + type: AccidentWayIF, + description: "Accident type information (id and label)", + }) + accidentType: AccidentWayIF; +} + diff --git a/src/request-management/dto/expert-complete-car-body-form.dto.ts b/src/request-management/dto/expert-complete-car-body-form.dto.ts new file mode 100644 index 0000000..9bebef5 --- /dev/null +++ b/src/request-management/dto/expert-complete-car-body-form.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { LocationDto } from "./create-request-management.dto"; +import { DescriptionDto } from "./create-request-management.dto"; +import { InitialFormDto, CarBodyFormDto } from "./create-request-management.dto"; + +/** + * Comprehensive form for experts to fill all information at once for IN_PERSON CAR_BODY files + */ +export class ExpertCompleteCarBodyFormDto { + // First Party Information + @ApiProperty({ + description: "First party phone number", + example: "09123456789", + }) + firstPartyPhoneNumber: string; + + @ApiProperty({ type: InitialFormDto }) + firstPartyInitialForm: InitialFormDto; + + @ApiProperty({ type: CarBodyFormDto }) + carBodyForm: CarBodyFormDto; + + @ApiProperty({ type: AddPlateDto }) + firstPartyPlate: AddPlateDto; + + @ApiProperty({ type: LocationDto }) + firstPartyLocation: LocationDto; + + @ApiProperty({ type: DescriptionDto }) + firstPartyDescription: DescriptionDto; +} + diff --git a/src/request-management/dto/expert-complete-third-party-form.dto.ts b/src/request-management/dto/expert-complete-third-party-form.dto.ts new file mode 100644 index 0000000..8957f82 --- /dev/null +++ b/src/request-management/dto/expert-complete-third-party-form.dto.ts @@ -0,0 +1,64 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { LocationDto } from "./create-request-management.dto"; +import { DescriptionDto } from "./create-request-management.dto"; +import { InitialFormDto } from "./create-request-management.dto"; + +/** + * Second party information for expert-completed THIRD_PARTY forms + */ +export class ExpertSecondPartyInfoDto { + @ApiProperty({ + description: "Second party phone number", + example: "09123456789", + }) + phoneNumber: string; + + @ApiProperty({ type: InitialFormDto }) + initialForm: InitialFormDto; + + @ApiProperty({ type: AddPlateDto }) + plate: AddPlateDto; + + @ApiProperty({ type: LocationDto }) + location: LocationDto; + + @ApiProperty({ type: DescriptionDto }) + description: DescriptionDto; +} + +/** + * Comprehensive form for experts to fill all information at once for IN_PERSON THIRD_PARTY files + */ +export class ExpertCompleteThirdPartyFormDto { + // First Party Information + @ApiProperty({ + description: "First party phone number", + example: "09123456789", + }) + firstPartyPhoneNumber: string; + + @ApiProperty({ type: InitialFormDto }) + firstPartyInitialForm: InitialFormDto; + + @ApiProperty({ type: AddPlateDto }) + firstPartyPlate: AddPlateDto; + + @ApiProperty({ type: LocationDto }) + firstPartyLocation: LocationDto; + + @ApiProperty({ type: DescriptionDto }) + firstPartyDescription: DescriptionDto; + + // Second Party Information + @ApiProperty({ type: ExpertSecondPartyInfoDto }) + secondParty: ExpertSecondPartyInfoDto; + + // Guilty Party + @ApiProperty({ + description: "Phone number of the guilty party. Must match either first or second party phone number.", + example: "09123456789", + }) + guiltyPartyPhoneNumber: string; +} + diff --git a/src/request-management/dto/expert-initiated.dto.ts b/src/request-management/dto/expert-initiated.dto.ts new file mode 100644 index 0000000..d6a5a92 --- /dev/null +++ b/src/request-management/dto/expert-initiated.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsEnum, IsOptional, IsString } from "class-validator"; +import { CreationMethod } from "../entities/schema/request-management.schema"; + +export class CreateExpertInitiatedFileDto { + @ApiProperty({ + description: "Type of blame file", + enum: ["THIRD_PARTY", "CAR_BODY"], + example: "THIRD_PARTY", + }) + @IsEnum(["THIRD_PARTY", "CAR_BODY"]) + type: "THIRD_PARTY" | "CAR_BODY"; + + @ApiProperty({ + description: "Method of creation", + enum: CreationMethod, + example: CreationMethod.IN_PERSON, + }) + @IsEnum(CreationMethod) + creationMethod: CreationMethod; + + // Phone numbers for LINK method - users will access files via normal login + @ApiPropertyOptional({ + description: "First party phone number. Required for LINK method.", + example: "09123456789", + }) + @IsOptional() + @IsString() + firstPartyPhoneNumber?: string; + + @ApiPropertyOptional({ + description: "Second party phone number. Required for LINK method when type is THIRD_PARTY.", + example: "09187654321", + }) + @IsOptional() + @IsString() + secondPartyPhoneNumber?: string; +} + diff --git a/src/request-management/dto/update-request-management.dto.ts b/src/request-management/dto/update-request-management.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/request-management/dto/user-reply.dto.ts b/src/request-management/dto/user-reply.dto.ts new file mode 100644 index 0000000..250381e --- /dev/null +++ b/src/request-management/dto/user-reply.dto.ts @@ -0,0 +1,52 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { RequestManagementModel } from "../entities/schema/request-management.schema"; + +export class UserReplyDto { + public _id: string; + + @ApiProperty({ required: true }) + isAccept: boolean; + + @ApiProperty({ required: true }) + signId: string; + + constructor(requestId: string) { + this._id = requestId; + } +} + +export class DetailsReplyDtoRs { + public fields: Object; + public actorFullName: string; + public description: string; + public dateOfReply: string; + public timeOfReply: string; + public isGuiltyUser: boolean; + public firstPartyAccept: boolean; + public secondPartyAccept: boolean; + public status: string; + public expertResendReply: any; + public expertSubmitReply: any; + public expertSubmitReplyFinal: any; + public theParties: any; + public type: string; + + constructor(request: RequestManagementModel, details: any) { + const finalReply = request.expertSubmitReply; + + this.fields = finalReply.fields; + this.description = finalReply.description; + this.firstPartyAccept = finalReply?.firstPartyComment?.isAccept; + this.secondPartyAccept = finalReply?.secondPartyComment?.isAccept; + this.actorFullName = details.actorDetail.CheckedRequest.fullName; + this.dateOfReply = details.actorDetail.Date.toLocaleDateString("fa-IR"); + this.timeOfReply = details.actorDetail.Date.toLocaleTimeString("fa-IR"); + this.isGuiltyUser = details.isGuilty; + this.status = request.blameStatus; + this.expertResendReply = request.expertResendReply; + this.expertSubmitReply = request.expertSubmitReply; + this.expertSubmitReplyFinal = request.expertSubmitReplyFinal; + this.theParties = details.theParties; + this.type = request.type || "THIRD_PARTY"; // Include type field + } +} diff --git a/src/request-management/entities/db-service/blame-document.db.service.ts b/src/request-management/entities/db-service/blame-document.db.service.ts new file mode 100644 index 0000000..a156500 --- /dev/null +++ b/src/request-management/entities/db-service/blame-document.db.service.ts @@ -0,0 +1,36 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { FilterQuery, Model, UpdateQuery } from 'mongoose'; +import { BlameDocumentModel } from '../schema/blame-document.schema'; + +@Injectable() +export class BlameDocumentDbService { + constructor( + @InjectModel(BlameDocumentModel.name) + private readonly blameDocumentModel: Model, + ) {} + + async create(createDto: Partial): Promise { + const newDocument = new this.blameDocumentModel(createDto); + return newDocument.save(); + } + + async findOne(filter: FilterQuery): Promise { + return this.blameDocumentModel.findOne(filter).lean(); + } + + async findAll(filter: FilterQuery): Promise { + return this.blameDocumentModel.find(filter).lean(); + } + + async findById(id: string): Promise { + return this.blameDocumentModel.findById(id).lean(); + } + + async findByIdAndUpdate( + id: string, + updateDto: UpdateQuery, + ): Promise { + return this.blameDocumentModel.findByIdAndUpdate(id, updateDto, { new: true }).lean(); + } +} \ No newline at end of file diff --git a/src/request-management/entities/db-service/blame-video.db.service.ts b/src/request-management/entities/db-service/blame-video.db.service.ts new file mode 100644 index 0000000..09cbac2 --- /dev/null +++ b/src/request-management/entities/db-service/blame-video.db.service.ts @@ -0,0 +1,26 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types } from "mongoose"; +import { BlameVideosModel } from "src/request-management/entities/schema/blame-video.schema"; + +export class BlameVideoDbService { + constructor( + @InjectModel(BlameVideosModel.name) + private readonly blameVideoModel: Model, + ) {} + + async create(video: BlameVideosModel): Promise { + return await this.blameVideoModel.create(video); + } + + async findOne(id: string): Promise { + return await this.blameVideoModel.findOne(new Types.ObjectId(id)); + } + + async findOneByRequestId(id: string): Promise { + return await this.blameVideoModel.findById(id); + } + + async findById(id: string): Promise { + return this.blameVideoModel.findById(id).lean(); + } +} diff --git a/src/request-management/entities/db-service/blame.voice.db.service.ts b/src/request-management/entities/db-service/blame.voice.db.service.ts new file mode 100644 index 0000000..38aee5d --- /dev/null +++ b/src/request-management/entities/db-service/blame.voice.db.service.ts @@ -0,0 +1,22 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types } from "mongoose"; +import { BlameVoicesModel } from "../schema/blame-voice.schema"; + +export class BlameVoiceDbService { + constructor( + @InjectModel(BlameVoicesModel.name) + private readonly blameVoiceModel: Model, + ) {} + + async create(Voice: BlameVoicesModel): Promise { + return await this.blameVoiceModel.create(Voice); + } + + async findOne(id: string): Promise { + return await this.blameVoiceModel.findOne(new Types.ObjectId(id)); + } + + async findById(id: string): Promise { + return this.blameVoiceModel.findById(id).lean(); + } +} diff --git a/src/request-management/entities/db-service/request-management.db.service.ts b/src/request-management/entities/db-service/request-management.db.service.ts new file mode 100644 index 0000000..07f6c0c --- /dev/null +++ b/src/request-management/entities/db-service/request-management.db.service.ts @@ -0,0 +1,94 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, Types, UpdateQuery } from "mongoose"; +import { RequestManagementModel } from "../schema/request-management.schema"; + +export class RequestManagementDbService { + constructor( + @InjectModel(RequestManagementModel.name) + private readonly requestModel: Model, + ) {} + + async create( + request: RequestManagementModel, + ): Promise { + return await this.requestModel.create(request); + } + + async findOne(id: string) { + return await this.requestModel.findOne(new Types.ObjectId(id)); + } + + async findAll(filter?: FilterQuery) { + return await this.requestModel.find({ ...filter }); + } + + async findAllAndPagination(query, currentPage, resPerPage?) { + const responsePerPage = resPerPage | 1; + const skipPge = responsePerPage * (Number(currentPage) - 1); + return await this.requestModel + .find(query) + .limit(responsePerPage) + .skip(skipPge); + } + + async countByFilter( + filter: FilterQuery, + ): Promise { + return await this.requestModel.countDocuments(filter); + } + + async findAndUpdate( + filter: FilterQuery, + update: UpdateQuery, + ) { + return await this.requestModel.findByIdAndUpdate(filter, update); + } + + async findOneAndUpdate( + filter: FilterQuery, + update: UpdateQuery, + options: { + new?: boolean; + upsert?: boolean; + runValidators?: boolean; + } = {}, + ): Promise { + try { + const updatedDoc = await this.requestModel.findOneAndUpdate( + filter, + update, + { + new: options.new ?? true, // Return updated doc by default + upsert: options.upsert ?? false, // Don’t create new if not found + runValidators: options.runValidators ?? true, + }, + ); + + return updatedDoc; + } catch (error) { + console.error( + `❌ [RequestManagementDbService] findOneAndUpdate failed`, + error, + ); + throw error; + } + } + + async findByIdAndUpdate( + id: string, + update: UpdateQuery, + ) { + return await this.requestModel.findByIdAndUpdate( + { _id: new Types.ObjectId(id) }, + update, + ); + } + + async aggregate(filter?) { + return await this.requestModel.aggregate(filter); + } + + async updateMany(filter?, update?) { + return await this.requestModel.updateMany(filter, update); + } +} diff --git a/src/request-management/entities/db-service/sign.db.service.ts b/src/request-management/entities/db-service/sign.db.service.ts new file mode 100644 index 0000000..e1e19c7 --- /dev/null +++ b/src/request-management/entities/db-service/sign.db.service.ts @@ -0,0 +1,23 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types } from "mongoose"; +import { UserSignModel } from "../schema/sign.schema"; + +export class UserSignDbService { + constructor( + @InjectModel(UserSignModel.name) + private readonly blameVoiceModel: Model, + ) {} + + async create(sign: UserSignModel): Promise { + return await this.blameVoiceModel.create(sign); + } + + async findOne(id: string): Promise { + return await this.blameVoiceModel.findOne(new Types.ObjectId(id)); + } + + async findById(id: string): Promise { + // Use your correct Model type + return this.blameVoiceModel.findById(id).lean(); + } +} diff --git a/src/request-management/entities/schema/blame-document.schema.ts b/src/request-management/entities/schema/blame-document.schema.ts new file mode 100644 index 0000000..d7b9454 --- /dev/null +++ b/src/request-management/entities/schema/blame-document.schema.ts @@ -0,0 +1,26 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; + +export enum BlameDocumentType { + NationalCertificate = "nationalCertificate", + CarCertificate = "carCertificate", + DrivingLicence = "drivingLicense", + CarGreenCard = "carGreenCard", +} + +@Schema({ collection: "blame-documents", versionKey: false, timestamps: true }) +export class BlameDocumentModel { + @Prop({ required: true }) + path: string; + + @Prop({ required: true }) + requestId: Types.ObjectId; + + @Prop({ required: true }) + fileName: string; + + @Prop({ required: true, type: String, enum: BlameDocumentType }) + documentType: BlameDocumentType; +} +export const BlameDocumentSchema = + SchemaFactory.createForClass(BlameDocumentModel); diff --git a/src/request-management/entities/schema/blame-video.schema.ts b/src/request-management/entities/schema/blame-video.schema.ts new file mode 100644 index 0000000..c16a1c9 --- /dev/null +++ b/src/request-management/entities/schema/blame-video.schema.ts @@ -0,0 +1,15 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; + +@Schema({ collection: "blame-videos", versionKey: false }) +export class BlameVideosModel { + @Prop() + path: string; + + @Prop() + requestId: Types.ObjectId; + + @Prop() + fileName: string; +} +export const BlameVideoSchema = SchemaFactory.createForClass(BlameVideosModel); diff --git a/src/request-management/entities/schema/blame-voice.schema.ts b/src/request-management/entities/schema/blame-voice.schema.ts new file mode 100644 index 0000000..34e15c8 --- /dev/null +++ b/src/request-management/entities/schema/blame-voice.schema.ts @@ -0,0 +1,24 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; + +export enum UploadContext { + INITIAL = 'INITIAL_UPLOAD', + EXPERT_RESEND = 'EXPERT_RESEND', +} + +@Schema({ collection: "blame-voice", versionKey: false }) +export class BlameVoicesModel { + @Prop() + path: string; + + @Prop() + requestId: Types.ObjectId; + + @Prop() + fileName: string; + + @Prop({ type: String, enum: UploadContext, default: UploadContext.INITIAL }) + context?: UploadContext; +} + +export const BlameVoiceSchema = SchemaFactory.createForClass(BlameVoicesModel); diff --git a/src/request-management/entities/schema/request-management.schema.ts b/src/request-management/entities/schema/request-management.schema.ts new file mode 100644 index 0000000..ed054fa --- /dev/null +++ b/src/request-management/entities/schema/request-management.schema.ts @@ -0,0 +1,465 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { + ResendFirstPartyDto, + ResendSecondPartyDto, +} from "src/expert-blame/dto/reply.dto"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; +import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum"; + +export class InitialForm { + @Prop({ required: false, default: false }) + expertOpinion?: boolean; + + @Prop({ required: false, default: false }) + imDamaged?: boolean; + + @Prop({ required: false, default: false }) + imGuilty?: boolean; +} + +export class FirstPartyFile { + @Prop() + descriptions?: string[]; + + @Prop() + voices?: string[]; + + @Prop() + firstPartyVideoId?: string; + + // CAR_BODY specific accident details + @Prop({ type: Date }) + accidentDate?: Date; + + @Prop() + accidentTime?: string; + + @Prop() + weatherCondition?: string; + + @Prop() + roadCondition?: string; + + @Prop() + lightCondition?: string; +} +export class CarDetail { + carName?: string; + carModel?: string; + carType?: string; + isNewCar?: boolean; + insurerBirthday?: string; + + driverBirthday?: string | null; +} + +export class InsuranceDetail { + insuranceId?: string; + insuranceCompany?: string; + financialCommitmentCeiling?: string; + startDate?: string; + endDate?: string; +} + +// CAR_BODY specific insurance details for the damaged party +export class CarBodyInsuranceDetail { + @Prop() + policyNumber?: string; + + @Prop() + startDate?: string; + + @Prop() + endDate?: string; + + @Prop() + insurerCompany?: string; + + @Prop({ type: [String] }) + coverages?: string[]; +} + +export class SecondPartyFile { + @Prop() + descriptions?: string[]; + + @Prop() + voices?: string[]; + + @Prop() + secondPartyVideoId?: string; +} + +export class FirstPartyClient { + clientName?: string; + clientId?: Types.ObjectId; +} + +export class SecondPartyClient { + clientName?: string; + clientId?: Types.ObjectId; +} + +export class FirstPartyDetail { + @Prop({ type: String }) + firstPartyId?: Types.ObjectId; + + @Prop({ type: String }) + firstPartyPhoneNumber?: string; + + @Prop({ type: String }) + firstPartyClientId?: string; + + @Prop({ + type: AddPlateDto, + description: "first add plate and send plate id to this field", + }) + firstPartyPlate?: AddPlateDto; + + @Prop({ type: CarDetail }) + firstPartyCarDetail?: CarDetail; + + @Prop({ type: InsuranceDetail }) + firstPartyInsuranceDetail?: InsuranceDetail; + + // Only used for CAR_BODY type requests – car body insurance info + @Prop({ type: CarBodyInsuranceDetail }) + firstPartyCarBodyInsuranceDetail?: CarBodyInsuranceDetail; + + @Prop({ type: FirstPartyFile }) + firstPartyClient?: FirstPartyClient; + + @Prop({ type: FirstPartyFile }) + firstPartyFile?: FirstPartyFile; + + @Prop({ type: FirstPartyFile }) + firstPartyInitialForm?: InitialForm; + + @Prop({ type: Boolean }) + firstPartyCertificate: boolean | null; +} + +export class SecondPartyDetail { + @Prop({ type: String }) + secondPartyId?: Types.ObjectId; + + @Prop({ type: String }) + secondPartyPhoneNumber?: string; + + @Prop({ type: SecondPartyClient }) + secondPartyClient?: SecondPartyClient; + + @Prop({ + type: AddPlateDto, + description: "secondParty add plate and send plate id to this field", + }) + secondPartyPlate?: AddPlateDto; + + @Prop() + secondPartyCarDetail?: CarDetail; + + @Prop({ type: InsuranceDetail }) + secondPartyInsuranceDetail?: InsuranceDetail; + + @Prop() + secondPartyPlateId?: string; + + @Prop({ type: InitialForm }) + secondPartyInitialForm: InitialForm; + + @Prop({ type: SecondPartyFile }) + secondPartyFiles?: SecondPartyFile; +} + +export class LocationDto { + @Prop({ type: Number, required: true }) + lat: number; + + @Prop({ type: Number, required: true }) + lon: number; +} + +export class AccidentWayIF { + @Prop({ required: true }) + id: string; + + @Prop({ required: true }) + label: string; +} +export class AccidentReasonIF { + @Prop({ required: true }) + id: string; + + @Prop({ required: true }) + label: string; + + @Prop({ required: true }) + fanavaran: number; +} + +export class FieldsInterface { + @Prop({ required: true, type: AccidentWayIF }) + accidentWay: AccidentWayIF; + + @Prop({ required: true, type: AccidentReasonIF }) + accidentReason: AccidentReasonIF; + + @Prop({ required: true, type: AccidentWayIF }) + accidentType: AccidentWayIF; +} + +export class SignDetail { + @Prop() + fileId: string; + + @Prop() + fileName: string; + + @Prop() + fileUrl: string; +} +export class PartiesComment { + @Prop({ required: true, type: Boolean }) + isAccept?: boolean; + + @Prop({ required: true }) + signDetail?: SignDetail; +} + +export class SubmitReply { + @Prop({ required: true }) + description: string; + + @Prop({ required: true, type: String }) + guiltyUserId: Types.ObjectId; + + @Prop({ required: true }) + fields: FieldsInterface; + + @Prop({ required: true, default: () => new Date() }) + submitTime: Date; + + @Prop({ type: PartiesComment }) + firstPartyComment?: PartiesComment; + + @Prop({ type: PartiesComment }) + secondPartyComment?: PartiesComment; + + @Prop() + systemResponse: string; +} + +export class ExpertResendParties { + userId?: Types.ObjectId; + description?: string; +} + +export class ExpertResendReply { + // TODO change DTOs to dynamic Exmple SubmitReply Parties comment + @Prop() + firstParty?: ResendFirstPartyDto; + + @Prop() + secondParty?: ResendSecondPartyDto; +} + +export class ActorLockDetails { + @Prop({ type: String }) + fullName?: string; + + @Prop({ type: Types.ObjectId }) + actorId?: Types.ObjectId; +} + +export enum CreationMethod { + NORMAL = "NORMAL", + LINK = "LINK", + IN_PERSON = "IN_PERSON", +} + +export enum FilledBy { + CUSTOMER = "customer", + EXPERT = "expert", +} + +export class ExpertLinkInfo { + @Prop({ type: String, required: true }) + token: string; + + @Prop({ type: Date, required: true }) + expiresAt: Date; + + @Prop({ type: Boolean, default: false }) + isUsed: boolean; + + @Prop({ type: Boolean, default: false }) + isExpired: boolean; + + @Prop({ type: Date }) + generatedAt: Date; + + @Prop({ type: String }) + sentTo?: string; // Phone number or email + + @Prop({ type: Number, default: 0 }) + openCount: number; + + @Prop({ type: Date }) + lastOpenedAt?: Date; + + @Prop({ type: Date }) + usedAt?: Date; +} + +export class FileHistoryEvent { + @Prop({ type: String, required: true }) + eventType: string; // "LINK_SENT", "LINK_OPENED", "FILLED_BY_CUSTOMER", "FILLED_BY_EXPERT", "ASSIGNED_TO_EXPERT", "EVALUATION_STARTED", "EVALUATION_COMPLETED", "USER_ACCEPTED", "USER_REJECTED", "SENT_TO_FANAVARAN", "CODE_RECEIVED" + + @Prop({ type: Types.ObjectId }) + actorId?: Types.ObjectId; + + @Prop({ type: String }) + actorName?: string; + + @Prop({ type: String }) + actorType?: string; // "expert", "damage_expert", "customer" + + @Prop({ type: Date, default: () => new Date() }) + timestamp: Date; + + @Prop({ type: Object }) + metadata?: any; // Additional event-specific data +} + +@Schema({ _id: false }) +export class FileRating { + @Prop({ type: Number, min: 0, max: 5 }) + collisionMethodAccuracy: number; + + @Prop({ type: Number, min: 0, max: 5 }) + evaluationTimeliness: number; + + @Prop({ type: Number, min: 0, max: 5 }) + accidentCauseAccuracy: number; + + @Prop({ type: Number, min: 0, max: 5 }) + guiltyVehicleIdentification: number; +} + +// TODO clean all sub Object to other files +export type RequestManagementDocs = RequestManagementModel & Document; + +@Schema({ + collection: "requests-management", + versionKey: false, + timestamps: true, + autoIndex: true, + _id: true, +}) +export class RequestManagementModel { + @Prop({ type: String, required: false }) + type: string; + + @Prop({ type: String, required: true, index: { unique: true } }) + requestNumber: string; + + @Prop() + blameStatus: ReqBlameStatus | StepsEnum; + + @Prop() + steps?: StepsEnum[]; + + @Prop() + userComment?: string; + + @Prop() + currentStep: StepsEnum; + + @Prop() + nextStep?: StepsEnum; + + @Prop({ description: "first step" }) + initialForm?: InitialForm; + + @Prop({ required: false }) + firstPartyDetails?: FirstPartyDetail; + + @Prop({ required: false, type: Types.ObjectId }) + sanHubId?: Types.ObjectId; + + @Prop({ required: false }) + secondPartyDetails?: SecondPartyDetail; + + @Prop({ required: false, description: "third step call" }) + firstPartyLocation?: LocationDto; + + @Prop({ required: false }) + secondPartyLocation?: LocationDto; + + @Prop({ type: Date, default: Date.now }) + createdAt?: Date | string; + + @Prop({ type: Date, default: Date.now }) + updatedAt?: Date; + + @Prop() + lockFile?: boolean; + + @Prop({ type: Date }) + lockTime?: Date | string | number; + + @Prop({ type: Date }) + unlockTime?: Date | number; + + @Prop() + actorLocked?: ActorLockDetails; + + @Prop() + actorsChecker?: []; + + @Prop({ required: false }) + expertSubmitReply?: SubmitReply | null; + + @Prop() + expertResendReply?: ExpertResendReply; + + @Prop() + expertSubmitReplyFinal?: ExpertResendReply; + + @Prop({ type: FileRating, required: false }) + rating?: FileRating; + + @Prop({ type: Date, required: false }) + autoCloseTriggerTime?: Date; + + @Prop({ type: Boolean, default: false }) + isHandledStatsUpdated?: boolean; + + // Expert-initiated file tracking + @Prop({ type: Boolean, default: false }) + expertInitiated?: boolean; + + @Prop({ type: Types.ObjectId }) + initiatedBy?: Types.ObjectId; // Expert who started the process + + @Prop({ type: String, enum: FilledBy }) + filledBy?: FilledBy; // Who filled the information + + @Prop({ type: String, enum: CreationMethod, default: CreationMethod.NORMAL }) + creationMethod?: CreationMethod; // How the file was created + + @Prop({ type: ExpertLinkInfo }) + expertLink?: ExpertLinkInfo; // Link information if created via link + + @Prop({ type: [FileHistoryEvent], default: [] }) + history?: FileHistoryEvent[]; // Timeline of events + + @Prop({ type: Types.ObjectId }) + assignedExpertId?: Types.ObjectId; // Expert assigned for evaluation (should be same as initiatedBy for expert-initiated files) +} + +export const RequestManagementSchema = SchemaFactory.createForClass( + RequestManagementModel, +); diff --git a/src/request-management/entities/schema/sign.schema.ts b/src/request-management/entities/schema/sign.schema.ts new file mode 100644 index 0000000..c64de3a --- /dev/null +++ b/src/request-management/entities/schema/sign.schema.ts @@ -0,0 +1,19 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; + +@Schema({ collection: "blame-sign", versionKey: false }) +export class UserSignModel { + @Prop() + path: string; + + @Prop() + userId: string; + + @Prop() + requestId: Types.ObjectId; + + @Prop() + fileName: string; +} + +export const UserSignSchema = SchemaFactory.createForClass(UserSignModel); diff --git a/src/request-management/expert-initiated.controller.ts b/src/request-management/expert-initiated.controller.ts new file mode 100644 index 0000000..3845975 --- /dev/null +++ b/src/request-management/expert-initiated.controller.ts @@ -0,0 +1,275 @@ +import { extname } from "node:path"; +import { + Controller, + Post, + Body, + Param, + UseGuards, + Get, + Query, + UseInterceptors, + UploadedFile, +} from "@nestjs/common"; +import { + FileInterceptor, +} from "@nestjs/platform-express"; +import { diskStorage } from "multer"; +import { + ApiBearerAuth, + ApiBody, + ApiParam, + ApiQuery, + ApiTags, + ApiOperation, + ApiResponse, + ApiConsumes, +} from "@nestjs/swagger"; +import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { RequestManagementService } from "./request-management.service"; +import { CreateExpertInitiatedFileDto } from "./dto/expert-initiated.dto"; +import { ExpertCompleteThirdPartyFormDto } from "./dto/expert-complete-third-party-form.dto"; +import { ExpertCompleteCarBodyFormDto } from "./dto/expert-complete-car-body-form.dto"; +import { ExpertAccidentFieldsDto } from "./dto/expert-accident-fields.dto"; + +@ApiTags("expert-initiated-blame") +@Controller("expert-initiated-blame") +@ApiBearerAuth() +@UseGuards(LocalActorAuthGuard, RolesGuard) +@Roles(RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT) +export class ExpertInitiatedController { + constructor( + private readonly requestManagementService: RequestManagementService, + ) {} + + @Post("create") + @ApiOperation({ + summary: "Create expert-initiated blame file", + description: + "Expert creates an initial incomplete blame file. Can be IN_PERSON (expert fills all info) or LINK (expert sends link to users).", + }) + @ApiBody({ type: CreateExpertInitiatedFileDto }) + @ApiResponse({ + status: 201, + description: "File created successfully", + schema: { + type: "object", + properties: { + requestId: { type: "string" }, + }, + }, + }) + async createExpertInitiatedFile( + @CurrentUser() expert: any, + @Body() dto: CreateExpertInitiatedFileDto, + ) { + return await this.requestManagementService.createExpertInitiatedFile( + expert, + dto, + ); + } + + @Post("complete-form-third-party/:requestId") + @ApiOperation({ + summary: "Expert completes all information for IN_PERSON THIRD_PARTY file", + description: + "Expert fills all information at once for IN_PERSON THIRD_PARTY files. " + + "Includes first party, second party, and guilty party selection. " + + "Video and voice uploads are handled separately.", + }) + @ApiParam({ + name: "requestId", + description: "ID of the expert-initiated IN_PERSON THIRD_PARTY file", + }) + @ApiBody({ type: ExpertCompleteThirdPartyFormDto }) + @ApiResponse({ + status: 200, + description: "Form completed successfully. File is now ready for user signatures.", + }) + async completeThirdPartyForm( + @CurrentUser() expert: any, + @Param("requestId") requestId: string, + @Body() formData: ExpertCompleteThirdPartyFormDto, + ) { + return await this.requestManagementService.expertCompleteThirdPartyForm( + expert, + requestId, + formData, + ); + } + + @Post("complete-form-car-body/:requestId") + @ApiOperation({ + summary: "Expert completes all information for IN_PERSON CAR_BODY file", + description: + "Expert fills all information at once for IN_PERSON CAR_BODY files. " + + "Includes first party data and carBodyForm. " + + "Video and voice uploads are handled separately.", + }) + @ApiParam({ + name: "requestId", + description: "ID of the expert-initiated IN_PERSON CAR_BODY file", + }) + @ApiBody({ type: ExpertCompleteCarBodyFormDto }) + @ApiResponse({ + status: 200, + description: "Form completed successfully. File is now ready for user signature.", + }) + async completeCarBodyForm( + @CurrentUser() expert: any, + @Param("requestId") requestId: string, + @Body() formData: ExpertCompleteCarBodyFormDto, + ) { + return await this.requestManagementService.expertCompleteCarBodyForm( + expert, + requestId, + formData, + ); + } + + @Post("upload-video/:requestId") + @ApiOperation({ + summary: "Expert uploads video for expert-initiated file", + description: + "Expert uploads a single video file for the entire blame file. " + + "Only one video is needed per file (not per party). " + + "Only works for expert-initiated files.", + }) + @ApiParam({ + name: "requestId", + description: "ID of the expert-initiated file", + }) + @ApiConsumes("multipart/form-data") + @ApiBody({ + schema: { + type: "object", + properties: { + file: { + type: "string", + format: "binary", + }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("file", { + limits: { + fileSize: 20 * 1024 * 1024, // 20MB + }, + storage: diskStorage({ + destination: "./files/video", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `expert-${file.originalname}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @ApiResponse({ + status: 200, + description: "Video uploaded successfully", + }) + async uploadVideo( + @CurrentUser() expert: any, + @Param("requestId") requestId: string, + @UploadedFile() file?: Express.Multer.File, + ) { + return await this.requestManagementService.expertUploadVideo( + expert, + requestId, + file, + ); + } + + @Post("upload-voice/:requestId") + @ApiOperation({ + summary: "Expert uploads voice for expert-initiated file", + description: + "Expert uploads a single voice file for the entire blame file. " + + "Only one voice is needed per file (not per party). " + + "Only works for expert-initiated files.", + }) + @ApiParam({ + name: "requestId", + description: "ID of the expert-initiated file", + }) + @ApiConsumes("multipart/form-data") + @ApiBody({ + schema: { + type: "object", + properties: { + voice: { + type: "string", + format: "binary", + }, + }, + }, + }) + @UseInterceptors( + FileInterceptor("voice", { + limits: { + fileSize: 10 * 1024 * 1024, // 10MB + }, + storage: diskStorage({ + destination: "./files/voice", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const flname = file.originalname.split(".")[0]; + const filename = `expert-${flname}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @ApiResponse({ + status: 200, + description: "Voice uploaded successfully", + }) + async uploadVoice( + @CurrentUser() expert: any, + @Param("requestId") requestId: string, + @UploadedFile() voice?: Express.Multer.File, + ) { + return await this.requestManagementService.expertUploadVoice( + expert, + requestId, + voice, + ); + } + + @Post("add-accident-fields/:requestId") + @ApiOperation({ + summary: "Expert adds accident fields to expert-initiated file", + description: + "Expert adds accident way, reason, and type fields to an expert-initiated file. " + + "Can be used for both IN_PERSON and LINK-based files. " + + "If expertSubmitReply already exists, updates the fields. Otherwise, creates a new expertSubmitReply.", + }) + @ApiParam({ + name: "requestId", + description: "ID of the expert-initiated file", + }) + @ApiBody({ type: ExpertAccidentFieldsDto }) + @ApiResponse({ + status: 200, + description: "Accident fields added successfully", + }) + async addAccidentFields( + @CurrentUser() expert: any, + @Param("requestId") requestId: string, + @Body() fields: ExpertAccidentFieldsDto, + ) { + return await this.requestManagementService.expertAddAccidentFields( + expert, + requestId, + fields, + ); + } +} + diff --git a/src/request-management/request-management.controller.spec.ts b/src/request-management/request-management.controller.spec.ts new file mode 100644 index 0000000..fb8668e --- /dev/null +++ b/src/request-management/request-management.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { RequestManagementController } from "./request-management.controller"; +import { RequestManagementService } from "./request-management.service"; + +describe("RequestManagementController", () => { + let controller: RequestManagementController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [RequestManagementController], + providers: [RequestManagementService], + }).compile(); + + controller = module.get( + RequestManagementController, + ); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/request-management/request-management.controller.ts b/src/request-management/request-management.controller.ts new file mode 100644 index 0000000..6151187 --- /dev/null +++ b/src/request-management/request-management.controller.ts @@ -0,0 +1,403 @@ +import { extname } from "node:path"; +import { + Controller, + Get, + Post, + Body, + Param, + UseGuards, + UseInterceptors, + UploadedFile, + Req, + Put, + UploadedFiles, + Patch, + Query, +} from "@nestjs/common"; +import { + FileFieldsInterceptor, + FileInterceptor, +} from "@nestjs/platform-express"; +import { + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiParam, + ApiQuery, + ApiTags, +} from "@nestjs/swagger"; +import { Request } from "express"; +import { diskStorage } from "multer"; +import { GlobalGuard } from "src/auth/guards/global.guard"; +import { RolesGuard } from "src/auth/guards/role.guard"; +import { CurrentUser } from "src/decorators/user.decorator"; +import { Roles } from "src/decorators/roles.decorator"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { BlameDocumentType } from "./entities/schema/blame-document.schema"; +import { + CarBodyFormDto, + CarBodySecondForm, + CreateRequestDto, + DescriptionDto, + InitialFormDto, + LocationDto, +} from "./dto/create-request-management.dto"; +import { RequestManagementService } from "./request-management.service"; +import { InPersonVisitDto } from "src/claim-request-management/dto/in-person-visit.dto"; + +@Controller("blame-request-management") +@ApiTags("blame-request-management") +@ApiBearerAuth() +@UseGuards(GlobalGuard, RolesGuard) +@Roles(RoleEnum.USER) +export class RequestManagementController { + constructor( + private readonly requestManagementService: RequestManagementService, + ) {} + + @Post() + @UseGuards(GlobalGuard) + async createRequest( + @CurrentUser() user, + @Body() createRequestDto: CreateRequestDto, + ) { + return await this.requestManagementService.createRequest( + user, + createRequestDto.type, + ); + } + + @Post("/initial-form/:requestId") + @ApiParam({ name: "requestId" }) + @ApiBody({ type: InitialFormDto }) + @UseGuards(GlobalGuard) + async initialFrom( + @Body() + body: InitialFormDto, + @CurrentUser() user, + @Param("requestId") requestId, + ) { + return await this.requestManagementService.initialFormStep( + body, + user, + requestId, + ); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + file: { + type: "string", + format: "binary", + }, + }, + }, + }) + @ApiConsumes("multipart/form-data") + @UseInterceptors( + FileInterceptor("file", { + limits: { + fileSize: 20 * 1024 * 1024, + }, + storage: diskStorage({ + destination: "./files/video", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `${file.originalname}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @UseGuards(GlobalGuard) + @ApiParam({ name: "requestId" }) + @Post("upload-video/:requestId") + @UseGuards(GlobalGuard) + async videoUploader( + @Param("requestId") requestId, + @CurrentUser() user, + @UploadedFile() file?: Express.Multer.File, + ) { + return await this.requestManagementService.videoUploadStep( + file, + user, + requestId, + ); + } + + @Post("/add-plate/:requestId") + @ApiParam({ name: "requestId" }) + @ApiBody({ type: AddPlateDto }) + @UseGuards(GlobalGuard) + async addPlate( + @Body() + body: AddPlateDto, + @CurrentUser() user, + @Param("requestId") requestId, + ) { + return await this.requestManagementService.addPlateToRequest( + user, + body, + requestId, + ); + } + + @Post("/carbody-form/:requestId") + @ApiParam({ name: "requestId" }) + @ApiBody({ type: CarBodyFormDto }) + @UseGuards(GlobalGuard) + async carBodyForm( + @Body() + body: CarBodyFormDto, + @CurrentUser() user, + @Param("requestId") requestId, + ) { + return await this.requestManagementService.carBodyFormStep( + body, + user, + requestId, + ); + } + + @Post("/carbody-secondform/:requestId") + @ApiParam({ name: "requestId" }) + @ApiBody({ type: CarBodySecondForm }) + @UseGuards(GlobalGuard) + async carBodySecondForm( + @Body() + body: CarBodySecondForm, + @CurrentUser() user, + @Param("requestId") requestId, + ) { + return await this.requestManagementService.carBodySecondForm( + body, + user, + requestId, + ); + } + + @Post("/add-detail-location/:requestId") + @ApiParam({ name: "requestId" }) + @ApiBody({ type: LocationDto }) + @UseGuards(GlobalGuard) + async addLocation( + @Body() + body: LocationDto, + @CurrentUser() user, + @Param("requestId") requestId, + ) { + return await this.requestManagementService.addDetailToRequestLocation( + user.sub, + body, + requestId, + user, // Pass user object for expert verification + ); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + file: { + type: "string", + format: "binary", + }, + }, + }, + }) + @ApiConsumes("multipart/form-data") + @UseInterceptors( + FileInterceptor("file", { + limits: { + fileSize: 10 * 1024 * 1024, + }, + storage: diskStorage({ + destination: "./files/voice", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const flname = file.originalname.split(".")[0]; + const filename = `${flname}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @ApiParam({ name: "requestId" }) + @Post("upload-voice/:requestId") + @UseGuards(GlobalGuard) + async voiceUploader( + @Param("requestId") requestId, + @CurrentUser() user, + @UploadedFile() voice?: Express.Multer.File, + ) { + return await this.requestManagementService.voiceUploadStep( + user, + voice, + requestId, + ); + } + + @Post("/add-detail-description/:requestId") + @ApiParam({ name: "requestId" }) + @ApiBody({ type: DescriptionDto }) + @UseGuards(GlobalGuard) + async addDescription( + @Body() + body: DescriptionDto, + @Param("requestId") requestId, + @CurrentUser() user, + ) { + return await this.requestManagementService.addDescriptionToRequest( + user, + body, + requestId, + ); + } + + @Post("add-second-party-details/:phoneNumber/:requestId/:frontendRoutes") + @ApiParam({ name: "phoneNumber" }) + @ApiParam({ name: "requestId" }) + @ApiParam({ name: "frontendRoutes" }) + @UseGuards(GlobalGuard) + async addSecondPartyDetailsToRequest( + @Req() req: Request, + @Param("phoneNumber") phoneNumber, + @Param("requestId") requestId, + @Param("frontendRoutes") frontendRoutes, + @CurrentUser() firstPartyUser, + ) { + return await this.requestManagementService.addSecondPartyDetailsToRequest( + phoneNumber, + requestId, + firstPartyUser, + frontendRoutes, + req, + ); + } + + @Get("requests") + async requests(@CurrentUser() user) { + return await this.requestManagementService.myRequests(user.username); + } + + @Get("creatable-claims") + @UseGuards(GlobalGuard) + async getCreatableClaims(@CurrentUser() user) { + return await this.requestManagementService.getCreatableClaims(user); + } + + @Get("request/:requestId") + @ApiParam({ name: "requestId" }) + @Roles(RoleEnum.USER) + async checkParties(@CurrentUser() user, @Param("requestId") requestId) { + return await this.requestManagementService.requestDetails(user, requestId); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + sign: { + type: "string", + format: "binary", + }, + isAccept: { + type: "Boolean", + }, + }, + }, + }) + @ApiConsumes("multipart/form-data") + @UseInterceptors( + FileInterceptor("sign", { + limits: { + fileSize: 10 * 1024 * 1024, + }, + storage: diskStorage({ + destination: "./files/signs", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `${file.originalname.split(/[.,\s-]/)[0]}-${unique}${ex}`; + callback(null, filename); + }, + }), + }), + ) + @Put("request/reply/:requestId") + @ApiParam({ name: "requestId" }) + @UseGuards(GlobalGuard) + async userReply( + @Body("isAccept") isAccept: boolean, + @Param("requestId") requestId: string, + @CurrentUser() user, + @UploadedFile() sign?: Express.Multer.File, + ) { + const props = { requestId, user }; + return await this.requestManagementService.userReply( + props, + Boolean(isAccept), + sign, + ); + } + + @ApiBody({ + schema: { + type: "object", + properties: { + nationalCertificate: { type: "string", format: "binary" }, + carCertificate: { type: "string", format: "binary" }, + drivingLicense: { type: "string", format: "binary" }, + carGreenCard: { type: "string", format: "binary" }, + voice: { type: "string", format: "binary" }, + description: { type: "string" }, + }, + }, + }) + @ApiConsumes("multipart/form-data") + @UseInterceptors( + FileFieldsInterceptor( + [ + { name: "nationalCertificate", maxCount: 1 }, + { name: "carCertificate", maxCount: 1 }, + { name: "drivingLicense", maxCount: 1 }, + { name: "carGreenCard", maxCount: 1 }, + { name: "voice", maxCount: 1 }, + ], + { + limits: { fileSize: 10 * 1024 * 1024 }, + storage: diskStorage({ + destination: "./files/blame-resend-docs", + filename: (req, file, callback) => { + const unique = Date.now(); + const ex = extname(file.originalname); + const filename = `${file.fieldname}-${unique}${ex}`; + callback(null, filename); + }, + }), + }, + ), + ) + @UseGuards(GlobalGuard) + @ApiParam({ name: "requestId" }) + @Put("request/resend/:requestId") + userResendReply( + @Body("description") description: string, + @Param("requestId") requestId: string, + @CurrentUser() user: any, + @UploadedFiles() + files: { [key in BlameDocumentType | "voice"]?: Express.Multer.File[] }, + ) { + return this.requestManagementService.userResend( + files, + user, + requestId, + description, + ); + } +} diff --git a/src/request-management/request-management.module.ts b/src/request-management/request-management.module.ts new file mode 100644 index 0000000..5c0fe8f --- /dev/null +++ b/src/request-management/request-management.module.ts @@ -0,0 +1,72 @@ +import { forwardRef, Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { MulterModule } from "@nestjs/platform-express"; +import { ClaimRequestManagementModule } from "src/claim-request-management/claim-request-management.module"; +import { ClientModule } from "src/client/client.module"; +import { PlatesModule } from "src/plates/plates.module"; +import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service"; +import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; +import { SandHubModule } from "src/sand-hub/sand-hub.module"; +import { UsersModule } from "src/users/users.module"; +import { CronModule } from "src/utils/cron/cron.module"; +import { SmsManagerModule } from "src/utils/sms-manager/sms-manager.module"; +import { BlameDocumentDbService } from "./entities/db-service/blame-document.db.service"; +import { BlameVoiceDbService } from "./entities/db-service/blame.voice.db.service"; +import { UserSignDbService } from "./entities/db-service/sign.db.service"; +import { UserSignModel, UserSignSchema } from "./entities/schema/sign.schema"; +import { + BlameVideosModel, + BlameVideoSchema, +} from "./entities/schema/blame-video.schema"; +import { + BlameVoiceSchema, + BlameVoicesModel, +} from "./entities/schema/blame-voice.schema"; +import { BlameDocumentModel, BlameDocumentSchema } from "./entities/schema/blame-document.schema"; +import { + RequestManagementModel, + RequestManagementSchema, +} from "./entities/schema/request-management.schema"; +import { RequestManagementService } from "./request-management.service"; +import { RequestManagementController } from "./request-management.controller"; +import { ExpertInitiatedController } from "./expert-initiated.controller"; + +@Module({ + imports: [ + UsersModule, + ClientModule, + SandHubModule, + SmsManagerModule, + PlatesModule, + MulterModule.register({ + dest: "./files/video", + }), + MongooseModule.forFeature([ + { name: RequestManagementModel.name, schema: RequestManagementSchema }, + { name: BlameVideosModel.name, schema: BlameVideoSchema }, + { name: BlameVoicesModel.name, schema: BlameVoiceSchema }, + { name: UserSignModel.name, schema: UserSignSchema }, + { name: BlameDocumentModel.name, schema: BlameDocumentSchema }, + ]), + forwardRef(() => ClaimRequestManagementModule), + CronModule, + ], + controllers: [RequestManagementController, ExpertInitiatedController], + providers: [ + RequestManagementService, + BlameVideoDbService, + BlameVoiceDbService, + UserSignDbService, + RequestManagementDbService, + BlameDocumentDbService, + ], + exports: [ + RequestManagementService, + RequestManagementDbService, + BlameVideoDbService, + BlameVoiceDbService, + BlameDocumentDbService, + UserSignDbService, + ], +}) +export class RequestManagementModule {} diff --git a/src/request-management/request-management.service.spec.ts b/src/request-management/request-management.service.spec.ts new file mode 100644 index 0000000..e2718ce --- /dev/null +++ b/src/request-management/request-management.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { RequestManagementService } from "./request-management.service"; + +describe("RequestManagementService", () => { + let service: RequestManagementService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [RequestManagementService], + }).compile(); + + service = module.get(RequestManagementService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/request-management/request-management.service.ts b/src/request-management/request-management.service.ts new file mode 100644 index 0000000..e6bd6b5 --- /dev/null +++ b/src/request-management/request-management.service.ts @@ -0,0 +1,3146 @@ +import { + BadGatewayException, + BadRequestException, + ConflictException, + ForbiddenException, + HttpException, + HttpStatus, + Injectable, + InternalServerErrorException, + Logger, + NotFoundException, +} from "@nestjs/common"; +import { ExternalExceptionFilter } from "@nestjs/core/exceptions/external-exception-filter"; +import { Types } from "mongoose"; +import ShortUniqueId from "short-unique-id"; +import { ClientService } from "src/client/client.service"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; +import { + CarBodyFormDto, + CarBodySecondForm, + InitialFormDto, +} from "src/request-management/dto/create-request-management.dto"; +import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service"; +import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service"; +import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; +import { SandHubService } from "src/sand-hub/sand-hub.service"; +import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; +import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum"; +import { ExpertDbService } from "src/users/entities/db-service/expert.db.service"; +import { UserDbService } from "src/users/entities/db-service/user.db.service"; +import { AutoCloseRequestService } from "src/utils/cron/cron.service"; +import { SmsManagerService } from "src/utils/sms-manager/sms-manager.service"; +import { + DescriptionDto, + LocationDto, + RequestManagementDtoRs, +} from "./dto/create-request-management.dto"; +import { DetailsReplyDtoRs, UserReplyDto } from "./dto/user-reply.dto"; +import { BlameDocumentDbService } from "./entities/db-service/blame-document.db.service"; +import { UserSignDbService } from "./entities/db-service/sign.db.service"; +import { RequestManagementModel } from "./entities/schema/request-management.schema"; +import { UploadContext } from "./entities/schema/blame-voice.schema"; +import { BlameDocumentType } from "./entities/schema/blame-document.schema"; +import { SandHubDbService } from "src/sand-hub/entity/db-service/sand-hub.db.service"; +import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service"; +import { + CreationMethod, + FilledBy, + FileHistoryEvent, + ExpertLinkInfo, +} from "./entities/schema/request-management.schema"; +import { + CreateExpertInitiatedFileDto, +} from "./dto/expert-initiated.dto"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +@Injectable() +export class RequestManagementService { + private readonly logger = new Logger(RequestManagementService.name); + + constructor( + private readonly requestManagementDbService: RequestManagementDbService, + private readonly blameDocumentDbService: BlameDocumentDbService, + private readonly blameVideoDbService: BlameVideoDbService, + private readonly userDbService: UserDbService, + private readonly sandHubService: SandHubService, + private readonly sandHubDbService: SandHubDbService, + private readonly clientService: ClientService, + private readonly blameVoiceDbService: BlameVoiceDbService, + private readonly userSign: UserSignDbService, + private readonly smsManagerService: SmsManagerService, + private readonly expertDbService: ExpertDbService, + private readonly autoCloseRequestService: AutoCloseRequestService, + private readonly claimRequestManagementDbService: ClaimRequestManagementDbService, + ) {} + + async createRequest(user, type) { + const reqNumber = new ShortUniqueId({ counter: 1 }); + const createRequest = await this.requestManagementDbService.create({ + firstPartyDetails: { + firstPartyId: user.sub, + firstPartyPhoneNumber: user.username, + firstPartyCertificate: null, + }, + currentStep: StepsEnum.createRequest, + nextStep: + type === "THIRD_PARTY" + ? StepsEnum.F_InitialForm + : StepsEnum.CarBodyForm, + requestNumber: reqNumber.rnd(), + blameStatus: ReqBlameStatus.PendingForFirstParty, + lockFile: false, + type, + }); + return { requestId: createRequest["_id"] }; + } + + async initialFormStep( + reqBody: InitialFormDto, + user, + requestId, + ): Promise { + const foundRequest = + await this.requestManagementDbService.findOne(requestId); + + if (!foundRequest) { + throw new NotFoundException("Request not found"); + } + + // For LINK-based expert-initiated files, verify user is the correct party + if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) { + // User should already be set in firstPartyDetails or secondPartyDetails + const isFirstParty = foundRequest.firstPartyDetails?.firstPartyId?.toString() === user.sub || + foundRequest.firstPartyDetails?.firstPartyPhoneNumber === user.username; + const isSecondParty = foundRequest.secondPartyDetails?.secondPartyId?.toString() === user.sub || + foundRequest.secondPartyDetails?.secondPartyPhoneNumber === user.username; + + if (!isFirstParty && !isSecondParty) { + throw new ForbiddenException( + "You are not authorized to complete this file. This file was created by an expert for specific parties.", + ); + } + } + + if ( + foundRequest?.secondPartyDetails?.secondPartyPhoneNumber == user.username + ) { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + "secondPartyDetails.secondPartyId": user.sub, + blameStatus: ReqBlameStatus.PendingForSecondParty, + "secondPartyDetails.secondPartyInitialForm": reqBody, + currentStep: StepsEnum.S_InitialForm, + nextStep: StepsEnum.S_addPlate, + $push: { steps: StepsEnum.S_InitialForm }, + }, + ); + + // Add history for expert-initiated files + if (foundRequest.expertInitiated) { + await this.addHistoryEvent( + requestId, + "SECOND_PARTY_FORM_COMPLETED", + new Types.ObjectId(user.sub), + undefined, + foundRequest.filledBy === FilledBy.EXPERT ? "expert" : "customer", + ); + } + + return new RequestManagementDtoRs( + foundRequest.firstPartyDetails.firstPartyId, + user.sub, + StepsEnum.S_InitialForm, + StepsEnum.S_addPlate, + StepsEnum.S_addPlate, + requestId, + ); + } else if ( + foundRequest?.firstPartyDetails?.firstPartyPhoneNumber == user.username || + (foundRequest.expertInitiated && + foundRequest.creationMethod === CreationMethod.LINK && + foundRequest.firstPartyDetails?.firstPartyId?.toString() === user.sub) + ) { + // Normal flow or link-based - set full firstPartyDetails + const updatePayload: any = { + blameStatus: ReqBlameStatus.PendingForFirstParty, + firstPartyDetails: { + firstPartyId: user.sub, + firstPartyPhoneNumber: user.username, + firstPartyInitialForm: reqBody, + }, + currentStep: StepsEnum.F_InitialForm, + nextStep: StepsEnum.F_videoUpload, + $push: { steps: StepsEnum.F_InitialForm }, + }; + + // For link-based expert-initiated files, ensure filledBy is set + if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) { + updatePayload.$set = { filledBy: FilledBy.CUSTOMER }; + } + + const createRequestFile = + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + // Add history for expert-initiated link-based files + if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) { + await this.addHistoryEvent( + requestId, + "FIRST_PARTY_FORM_COMPLETED", + new Types.ObjectId(user.sub), + undefined, + "customer", + ); + } + + return new RequestManagementDtoRs( + createRequestFile.firstPartyDetails.firstPartyId, + null, + StepsEnum.F_InitialForm, + StepsEnum.F_videoUpload, + "Add video", + createRequestFile._id, + ); + } else { + // If none of the conditions match, throw an error + throw new ForbiddenException( + "You are not authorized to complete this form. Please ensure you are the correct party or using a valid link.", + ); + } + } + + async videoUploadStep( + file, + user, + requestId, + ): Promise { + const request = await this.requestManagementDbService.findOne(requestId); + + if (!request) { + throw new NotFoundException("Request not found"); + } + + // Check phone number match for normal flow + if (request?.firstPartyDetails?.firstPartyPhoneNumber !== user.username) { + throw new ForbiddenException("the request doesnt belong you"); + } + + try { + if (request.steps.includes(StepsEnum.F_videoUpload) || request.steps.includes(StepsEnum.CarBodyVideo)) + throw new BadRequestException("video steps has exist"); + + const videoDocument = await this.blameVideoDbService.create({ + fileName: file.filename, + path: file.path, + requestId, + }); + + // Determine the step to push and next step based on request type + const stepToPush = request.type === "THIRD_PARTY" ? StepsEnum.F_videoUpload : StepsEnum.CarBodyVideo; + const currentStepValue = request.type === "THIRD_PARTY" ? StepsEnum.F_videoUpload : StepsEnum.CarBodyVideo; + const nextStepValue = StepsEnum.F_addPlate; + + const updated = await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + $push: { steps: stepToPush }, + currentStep: currentStepValue, + "firstPartyDetails.firstPartyFile.firstPartyVideoId": + videoDocument["_doc"]._id, + nextStep: nextStepValue, + }, + ); + + if (updated == undefined) return new BadGatewayException("update failed"); + + return new RequestManagementDtoRs( + user.sub, + null, + currentStepValue, + nextStepValue, + "please add plate", + requestId, + ); + } catch (err) { + return new HttpException(err.message, err.status); + } + } + + async addPlate( + sandHubReport: any, + request: any, + user: any, + body: AddPlateDto, + partyType: "firstParty" | "secondParty", + ) { + const clientName = sandHubReport?.CompanyName; + const companyCode = sandHubReport?.CompanyCode; + + const client = + await this.clientService.findClientWithCompanyCode(+companyCode); + + if (!client) { + throw new HttpException("Client not found", HttpStatus.CONFLICT); + } + + const partyDetails = + partyType === "firstParty" ? "firstPartyDetails" : "secondPartyDetails"; + const stepsEnum = + partyType === "firstParty" ? StepsEnum.F_addPlate : StepsEnum.S_addPlate; + const currentStep = + partyType === "firstParty" ? StepsEnum.F_addPlate : StepsEnum.S_addPlate; + + // Determine next step based on request type + let nextStep: StepsEnum; + if (request.type === "CAR_BODY") { + // For CAR_BODY, after plate always go to location + // (Form and video were already completed before plate) + nextStep = StepsEnum.F_addLocation; + } else { + // THIRD_PARTY or default flow + nextStep = partyType === "firstParty" + ? StepsEnum.F_addLocation + : StepsEnum.S_addLocation; + } + + try { + await this.userDbService.findOneAndUpdate( + { _id: user.sub }, + { clientKey: client["_doc"]._id }, + ); + + const sandHubDocData = { + ...sandHubReport, + plate: body.plate, + nationalCode: body.nationalCodeOfInsurer, + }; + const sandHubDoc = await this.sandHubService.sandHubDocumentCreator( + user.sub, + request["_doc"]._id, + sandHubDocData, + ); + + if (partyType === "firstParty") { + await this.requestManagementDbService.findAndUpdate( + { _id: request._id }, + { + sanHubId: sandHubDoc["_doc"]._id, + }, + ); + } + + // Build $set object with all field updates + const setFields: any = { + currentStep: currentStep, + nextStep: nextStep, + [`${partyDetails}.nationalCodeOfInsurer`]: body.nationalCodeOfInsurer, + [`${partyDetails}.${partyType}Plate`]: body.plate, + [`${partyDetails}.${partyType}CarDetail.carName`]: + sandHubReport.MapTypNam, + [`${partyDetails}.${partyType}CarDetail.insurerBirthday`]: + body.insurerBirthday, + [`${partyDetails}.${partyType}CarDetail.driverBirthday`]: + body.driverBirthday, + [`${partyDetails}.${partyType}CarDetail.carType`]: `${sandHubReport.UsageField} / ${sandHubReport.MapUsageName || "-"}`, + [`${partyDetails}.${partyType}CarDetail.isNewCar`]: body.isNewCar, + [`${partyDetails}.${partyType}Client.clientId`]: client["_doc"]._id, + [`${partyDetails}.${partyType}Client.clientName`]: clientName, + [`${partyDetails}.${partyType}Certificate`]: body.userNoCertificate, + [`${partyDetails}.${partyType}InsuranceDetail.insuranceId`]: + sandHubReport.LastCompanyDocumentNumber, + [`${partyDetails}.${partyType}InsuranceDetail.insuranceCompany`]: + clientName, + [`${partyDetails}.${partyType}InsuranceDetail.financialCommitmentCeiling`]: + sandHubReport.FinancialCvrCptl, + [`${partyDetails}.${partyType}InsuranceDetail.startDate`]: + sandHubReport.IssueDate, + [`${partyDetails}.${partyType}InsuranceDetail.endDate`]: + sandHubReport.EndDate, + }; + + // For CAR_BODY type, also fetch and persist mocked car body insurance info + if (request.type === "CAR_BODY" && partyType === "firstParty") { + const carBodyInfo = await this.mockCarBodyInsuranceInquiry( + request._id, + body.plate, + body.nationalCodeOfInsurer, + ); + + this.logger.log( + `[CAR_BODY] Saving car body insurance data to blame file ${request._id}:`, + JSON.stringify(carBodyInfo, null, 2), + ); + + // Add car body insurance fields to $set + setFields["firstPartyDetails.firstPartyCarBodyInsuranceDetail.policyNumber"] = + carBodyInfo.policyNumber; + setFields["firstPartyDetails.firstPartyCarBodyInsuranceDetail.startDate"] = + carBodyInfo.startDate; + setFields["firstPartyDetails.firstPartyCarBodyInsuranceDetail.endDate"] = + carBodyInfo.endDate; + setFields["firstPartyDetails.firstPartyCarBodyInsuranceDetail.insurerCompany"] = + carBodyInfo.insurerCompany; + setFields["firstPartyDetails.firstPartyCarBodyInsuranceDetail.coverages"] = + carBodyInfo.coverages; + } + + // Build final update payload with proper MongoDB operators + const updatePayload: any = { + $push: { steps: stepsEnum }, + $set: setFields, + }; + + await this.requestManagementDbService.findAndUpdate( + { _id: request._id }, + updatePayload, + ); + } catch (er) { + this.logger.error(er); + throw new InternalServerErrorException( + "Failed to update request with plate details.", + ); + } + + const nextStepMessage = `Please add location - clientName is ${clientName}`; + + return new RequestManagementDtoRs( + request?.firstPartyDetails?.firstPartyId, + request?.secondPartyDetails?.secondPartyId || null, + stepsEnum, + nextStep, + nextStepMessage, + request._id, + ); + } + + async addPlateToRequest(user: any, body: AddPlateDto, requestId: string) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + if ( + (body.nationalCodeOfInsurer === body.nationalCodeOfDriver || + body.insurerLicense === body.driverLicense) && + body.driverIsInsurer === false + ) { + throw new BadRequestException( + "Insurer and Driver should be two different persons in this mode.", + ); + } + + // Determine if the current user is the first party + // Check if nextStep is firstParty-addPlate OR if user is the first party phone number + const isFirstParty = + (request.nextStep === StepsEnum.F_addPlate || + request.currentStep === StepsEnum.F_addPlate) && + request.firstPartyDetails.firstPartyPhoneNumber === user.username; + + if (!isFirstParty) { + const firstPartyDetails = request.firstPartyDetails as any; + if ( + firstPartyDetails?.firstPartyPlate || + firstPartyDetails?.nationalCodeOfInsurer + ) { + const isSameNationalCode = + firstPartyDetails.nationalCodeOfInsurer === + body.nationalCodeOfInsurer; + + const isSamePlate = + JSON.stringify(firstPartyDetails.firstPartyPlate) === + JSON.stringify(body.plate); + + if (isSameNationalCode || isSamePlate) { + throw new ConflictException( + "The plate and national code for the second party cannot be the same as the first party.", + ); + } + } + } + + // --- Proceed with existing logic if the check passes --- + // 1) Main third-party/block inquiry (existing behavior) + const sanHubResponse = await this.sandHubService.getSandHubResponse({ + plate: body.plate, + nationalCodeOfInsurer: body.nationalCodeOfInsurer, + }); + + if (sanHubResponse.Error) { + throw new HttpException( + sanHubResponse.Error.Message, + HttpStatus.BAD_REQUEST, + ); + } + + // 2) Personal inquiry (new shared SandHub API) + // This is a best-effort call; if it fails we log and continue. + try { + const personalInquiry = await this.sandHubService.getPersonalInquiry( + body.nationalCodeOfInsurer, + body.insurerBirthday, + ); + this.logger.log( + `[SANDHUB] Personal inquiry succeeded for nationalCode=${body.nationalCodeOfInsurer}: ${JSON.stringify( + personalInquiry, + )}`, + ); + // NOTE: We are not persisting this data yet; once the exact fields + // are finalized, we can map and store them on the request document. + } catch (err) { + this.logger.error( + `[SANDHUB] Personal inquiry failed for nationalCode=${body.nationalCodeOfInsurer}: ${err?.message || err}`, + ); + } + + return await this.addPlate( + sanHubResponse, + request, + user, + body, + isFirstParty ? "firstParty" : "secondParty", + ); + } + + async carBodyFormStep( + reqBody: CarBodyFormDto, + user, + requestId, + ): Promise { + const foundRequest = + await this.requestManagementDbService.findOne(requestId); + + if (!foundRequest) { + throw new NotFoundException("Request not found"); + } + + // Validate that this is a CAR_BODY type request + if (foundRequest?.type !== "CAR_BODY") { + throw new BadRequestException( + "This endpoint is only for CAR_BODY type requests", + ); + } + + // Validate that the user is the first party (or expert filling their own file) + if ( + !foundRequest.expertInitiated && + foundRequest?.firstPartyDetails?.firstPartyPhoneNumber !== user.username + ) { + throw new ForbiddenException( + "Only the first party can complete this step", + ); + } + + // For CAR_BODY flow: after form, always go to video upload + // We'll assume guilty by default for car accidents (no second form needed) + const nextStep = StepsEnum.CarBodyVideo; + + const createRequestFile = + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + blameStatus: ReqBlameStatus.PendingForFirstParty, + carBodyForm: { + firstPartyId: user.sub, + firstPartyPhoneNumber: user.username, + carBodyForm: reqBody, + // Assume guilty by default for car accidents + isGuilty: reqBody.car ? true : false, + }, + currentStep: StepsEnum.CarBodyForm, + nextStep: nextStep, + $push: { steps: StepsEnum.CarBodyForm }, + }, + ); + + return new RequestManagementDtoRs( + createRequestFile.firstPartyDetails.firstPartyId, + null, + StepsEnum.CarBodyForm, + nextStep, + "Please upload accident video", + createRequestFile._id, + ); + } + + async carBodySecondForm( + reqBody: CarBodySecondForm, + user, + requestId, + ): Promise { + // This endpoint is deprecated - second form is no longer needed + // We now assume guilty by default in the first form + throw new BadRequestException( + "This endpoint is deprecated. The second form is no longer required. Users are assumed guilty by default for car accidents.", + ); + } + + async addDetailToRequestLocation( + userId: string, + body: LocationDto, + requestId, + user?: any, // Optional user object for expert verification + ) { + const findRequest = + await this.requestManagementDbService.findOne(requestId); + if (!findRequest) throw new NotFoundException("requestId not found"); + + // Allow first party only + if (findRequest.firstPartyDetails.firstPartyId == new Types.ObjectId(userId)) { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + firstPartyLocation: body, + currentStep: StepsEnum.F_addLocation, + nextStep: StepsEnum.F_addVoice, + $push: { steps: StepsEnum.F_addLocation }, // Ensure step is saved + }, + ); + + return new RequestManagementDtoRs( + findRequest.firstPartyDetails.firstPartyId, + findRequest?.secondPartyDetails?.secondPartyId || null, + StepsEnum.F_addLocation, + StepsEnum.F_addVoice, + "please add detail", + requestId, + ); + } + if ( + findRequest?.secondPartyDetails?.secondPartyId == + new Types.ObjectId(userId) + ) { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + secondPartyLocation: body, + currentStep: StepsEnum.S_addLocation, + nextStep: StepsEnum.S_addVoice, + $push: { steps: StepsEnum.S_addLocation }, // Ensure step is saved + }, + ); + return new RequestManagementDtoRs( + findRequest.firstPartyDetails.firstPartyId, + findRequest?.secondPartyDetails?.secondPartyId || null, + StepsEnum.S_addLocation, + StepsEnum.S_addVoice, + "please add detail", + requestId, + ); + } else { + throw new BadGatewayException("parties not allowed"); + } + } + + async voiceUploadStep(userId: any, voice: Express.Multer.File, requestId) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("request is not found"); + } + + const voiceDoc = await this.blameVoiceDbService.create({ + fileName: voice.filename, + path: voice.path, + requestId, + }); + + // Allow first party + if (request.firstPartyDetails.firstPartyId === userId.sub) { + try { + const updated = await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + currentStep: StepsEnum.F_addVoice, + nextStep: StepsEnum.F_addDescription, + $push: { + "firstPartyDetails.firstPartyFile.voices": voiceDoc["_id"], + steps: StepsEnum.F_addVoice, + }, + }, + ); + if (updated == undefined) { + return new BadGatewayException("update failed"); + } + return new RequestManagementDtoRs( + request.firstPartyDetails.firstPartyId, + request?.secondPartyDetails?.secondPartyId || null, + StepsEnum.F_addVoice, + StepsEnum.F_addDescription, + "please add description", + requestId, + ); + } catch (err) { + console.log(err); + throw new ExternalExceptionFilter(); + } + } + if (request?.secondPartyDetails?.secondPartyId === userId.sub) { + try { + const updated = await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + currentStep: StepsEnum.S_addVoice, + nextStep: StepsEnum.S_addDescription, + $push: { + "secondPartyDetails.secondPartyFiles.voices": voiceDoc["_id"], + steps: StepsEnum.S_addVoice, + }, + }, + ); + if (updated == undefined) { + return new BadGatewayException("update failed"); + } + return new RequestManagementDtoRs( + request.firstPartyDetails.firstPartyId, + request?.secondPartyDetails?.secondPartyId || null, + StepsEnum.S_addVoice, + StepsEnum.S_addDescription, + "add Description for SecondParty", + requestId, + ); + } catch (err) { + console.log(err); + throw new ExternalExceptionFilter(); + } + } + } + + async addDescriptionToRequest( + user: any, + body: DescriptionDto, + requestId: string, + ) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // --- First Party Flow --- + if (request.firstPartyDetails.firstPartyId == user.sub) { + // Build base update payload with proper MongoDB operators + const nextStepValue = + request.type === "THIRD_PARTY" + ? StepsEnum.F_addSecondPartyPhoneNumber + : StepsEnum.AddSignatures; + const blameStatusValue = + request.type === "THIRD_PARTY" + ? ReqBlameStatus.PendingForFirstParty + : ReqBlameStatus.WaitingForSignatures; + + // Ensure firstPartyFile exists and initialize descriptions array if needed + const updatePayload: any = { + $set: { + currentStep: StepsEnum.F_addDescription, + nextStep: nextStepValue, + blameStatus: blameStatusValue, + }, + $push: { + steps: StepsEnum.F_addDescription, + }, + }; + + // Initialize firstPartyFile.descriptions if it doesn't exist, then push description + if (!request.firstPartyDetails.firstPartyFile || !request.firstPartyDetails.firstPartyFile.descriptions) { + // Initialize the descriptions array if it doesn't exist + if (!request.firstPartyDetails.firstPartyFile) { + updatePayload.$set["firstPartyDetails.firstPartyFile"] = {}; + } + updatePayload.$set["firstPartyDetails.firstPartyFile.descriptions"] = [body.desc]; + } else { + // If descriptions array exists, push to it + updatePayload.$push["firstPartyDetails.firstPartyFile.descriptions"] = body.desc; + } + + // Add CAR_BODY specific fields if type is CAR_BODY + if (request.type === "CAR_BODY") { + if (body.accidentDate) { + updatePayload.$set["firstPartyDetails.firstPartyFile.accidentDate"] = body.accidentDate; + } + if (body.accidentTime) { + updatePayload.$set["firstPartyDetails.firstPartyFile.accidentTime"] = body.accidentTime; + } + if (body.weatherCondition) { + updatePayload.$set["firstPartyDetails.firstPartyFile.weatherCondition"] = body.weatherCondition; + } + if (body.roadCondition) { + updatePayload.$set["firstPartyDetails.firstPartyFile.roadCondition"] = body.roadCondition; + } + if (body.lightCondition) { + updatePayload.$set["firstPartyDetails.firstPartyFile.lightCondition"] = body.lightCondition; + } + } + + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + // For expert-initiated files, ensure auto-assignment and add history + if (request.expertInitiated) { + await this.ensureExpertAssignment(requestId); + await this.addHistoryEvent( + requestId, + "FIRST_PARTY_DESCRIPTION_COMPLETED", + new Types.ObjectId(user.sub), + undefined, + request.filledBy === FilledBy.EXPERT ? "expert" : "customer", + ); + } + + const nextStepMessage = request.type === "CAR_BODY" + ? "Please sign the document to complete your request" + : "add secondParty to request"; + + return new RequestManagementDtoRs( + request.firstPartyDetails.firstPartyId, + request?.secondPartyDetails?.secondPartyId || null, + StepsEnum.F_addDescription, + nextStepValue, + nextStepMessage, + requestId, + ); + } + + // --- Second Party Flow --- + if (request?.secondPartyDetails?.secondPartyId == user.sub) { + const firstPartyForm = request.firstPartyDetails.firstPartyInitialForm; + const secondPartyForm = request.secondPartyDetails.secondPartyInitialForm; + + const shouldAutoResolve = + (firstPartyForm?.imDamaged && secondPartyForm?.imGuilty) || + (secondPartyForm?.imDamaged && firstPartyForm?.imGuilty); + + let updatePayload: any; + + if (shouldAutoResolve) { + // Build the payload for the auto-resolve (agreement) flow + const guiltyPartyId = firstPartyForm.imGuilty + ? request.firstPartyDetails.firstPartyId + : request.secondPartyDetails.secondPartyId; + + updatePayload = { + $set: { + blameStatus: ReqBlameStatus.WaitingForSignatures, + currentStep: StepsEnum.S_addDescription, + nextStep: StepsEnum.AddSignatures, + expertSubmitReply: { + description: "منتظر امضای طرفین برای تایید توافق.", + guiltyUserId: guiltyPartyId, + submitTime: new Date(), + }, + }, + $push: { + steps: StepsEnum.S_addDescription, + }, + }; + + // Initialize secondPartyFiles.descriptions if it doesn't exist, then push description + if (!request.secondPartyDetails?.secondPartyFiles || !request.secondPartyDetails.secondPartyFiles.descriptions) { + // Initialize the descriptions array if it doesn't exist + if (!request.secondPartyDetails.secondPartyFiles) { + updatePayload.$set["secondPartyDetails.secondPartyFiles"] = {}; + } + updatePayload.$set["secondPartyDetails.secondPartyFiles.descriptions"] = [body.desc]; + } else { + // If descriptions array exists, push to it + updatePayload.$push["secondPartyDetails.secondPartyFiles.descriptions"] = body.desc; + } + + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + return new RequestManagementDtoRs( + request.firstPartyDetails.firstPartyId, + user.sub, + StepsEnum.S_addDescription, + ReqBlameStatus.WaitingForSignatures, + "Agreement detected. Awaiting signatures from both parties.", + requestId, + ); + } else { + // Build the payload for the standard (disagreement) flow + updatePayload = { + $set: { + blameStatus: ReqBlameStatus.UnChecked, + currentStep: StepsEnum.S_addDescription, + nextStep: StepsEnum.S_completed, + }, + $push: { + steps: StepsEnum.S_addDescription, + }, + }; + + // Initialize secondPartyFiles.descriptions if it doesn't exist, then push description + if (!request.secondPartyDetails?.secondPartyFiles || !request.secondPartyDetails.secondPartyFiles.descriptions) { + // Initialize the descriptions array if it doesn't exist + if (!request.secondPartyDetails.secondPartyFiles) { + updatePayload.$set["secondPartyDetails.secondPartyFiles"] = {}; + } + updatePayload.$set["secondPartyDetails.secondPartyFiles.descriptions"] = [body.desc]; + } else { + // If descriptions array exists, push to it + updatePayload.$push["secondPartyDetails.secondPartyFiles.descriptions"] = body.desc; + } + + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + // For expert-initiated files, ensure auto-assignment and add history + if (request.expertInitiated) { + await this.ensureExpertAssignment(requestId); + await this.addHistoryEvent( + requestId, + "SECOND_PARTY_DESCRIPTION_COMPLETED", + new Types.ObjectId(user.sub), + undefined, + request.filledBy === FilledBy.EXPERT ? "expert" : "customer", + ); + await this.addHistoryEvent( + requestId, + "READY_FOR_EXPERT_EVALUATION", + request.initiatedBy, + undefined, + "system", + ); + } + + return new RequestManagementDtoRs( + request.firstPartyDetails.firstPartyId, + user.sub, + StepsEnum.S_addSecondPartyPhoneNumber, + "expertOpinion", + "please wait... for expert reply", + requestId, + ); + } + } + + throw new ForbiddenException("User is not a party to this request."); + } + + async addSecondPartyDetailsToRequest( + phoneNumber, + requestId, + firstPartyUser, + frontendRoutes, + req, + ) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + if (request?.secondPartyDetails?.secondPartyId) { + throw new BadGatewayException("Second party has already been added"); + } + if (request.firstPartyDetails.firstPartyPhoneNumber === phoneNumber) { + throw new BadRequestException( + "Second party phone number cannot be the same as the first party", + ); + } + + // Only perform the update if the second party details don't exist yet. + if (!request.secondPartyDetails) { + const updatePayload = { + blameStatus: ReqBlameStatus.PendingForSecondParty, + "secondPartyDetails.secondPartyPhoneNumber": phoneNumber, + currentStep: StepsEnum.F_completed, + nextStep: StepsEnum.S_InitialForm, + $push: { + steps: StepsEnum.F_addSecondPartyPhoneNumber, + }, + }; + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + } + + // Send the SMS after the database update is complete. + const URL = `${process.env.URL}/${frontendRoutes}?token=${requestId}`; + try { + const smsRes = await this.smsManagerService.verifyLookUp({ + token: URL, + template: "yara724-invite-link", + receptor: phoneNumber, + }); + this.logger.log(smsRes); + } catch (er) { + this.logger.error("SMS sending failed:", er); + } + + return { url: URL }; + } + + async myRequests(phoneNumber) { + const res = { listOfFirstParty: [], listOfSecondParty: [] }; + const blameStatuses = [ + ReqBlameStatus.CheckedRequest, + ReqBlameStatus.UnChecked, + ReqBlameStatus.PendingForFirstParty, + ReqBlameStatus.PendingForSecondParty, + ReqBlameStatus.ReviewRequest, + ReqBlameStatus.CheckAgain, + ReqBlameStatus.UserPending, + ReqBlameStatus.CloseRequest, + ReqBlameStatus.WaitForUserAccept, + ReqBlameStatus.WaitingForSignatures, + ReqBlameStatus.InPersonVisit, + ]; + + const parties = await this.requestManagementDbService.findAll({ + $or: [ + { + $or: [ + { "firstPartyDetails.firstPartyPhoneNumber": phoneNumber }, + { "secondPartyDetails.secondPartyPhoneNumber": phoneNumber }, + ], + blameStatus: { $in: blameStatuses }, + }, + ], + }); + // TODO create a DTO for Response + parties.forEach((p) => { + if (p?.secondPartyDetails?.secondPartyPhoneNumber == phoneNumber) { + res.listOfSecondParty.push({ + id: p.id, + blameStatus: p.blameStatus, + requestNumber: p.requestNumber, + createdAt: new Date(p.createdAt) + .toLocaleString("fa-IR") + .split("،")[0], + timeCreatedAt: new Date(p.createdAt) + .toLocaleTimeString("fa-IR") + .split(",")[0], + type: p.type || "THIRD_PARTY", // Include type field + }); + } + + if (p.firstPartyDetails.firstPartyPhoneNumber == phoneNumber) { + console.log( + new Date(p.createdAt).toLocaleTimeString("fa-IR").split(","), + ); + res.listOfFirstParty.push({ + id: p.id, + blameStatus: p.blameStatus, + requestNumber: p.requestNumber, + createdAt: new Date(p.createdAt) + .toLocaleString("fa-IR") + .split("،")[0], + timeCreatedAt: new Date(p.createdAt) + .toLocaleTimeString("fa-IR") + .split(",")[0], + type: p.type || "THIRD_PARTY", // Include type field + }); + } + }); + return res; + } + + async getCreatableClaims(user: any) { + const userId = user.sub; + + const creatableBlameFiles = await this.requestManagementDbService.findAll({ + blameStatus: ReqBlameStatus.CloseRequest, + + $or: [ + { "firstPartyDetails.firstPartyId": userId }, + { "secondPartyDetails.secondPartyId": userId }, + ], + + "expertSubmitReply.guiltyUserId": { $ne: userId }, + }); + + if (!creatableBlameFiles || creatableBlameFiles.length === 0) { + return []; + } + + // Fetch existing claim files for all blame files in parallel + const claimFilesPromises = creatableBlameFiles.map((blameFile) => + this.claimRequestManagementDbService.findOne( + undefined, + { "blameFile._id": new Types.ObjectId(blameFile._id) }, + ), + ); + const existingClaimFiles = await Promise.all(claimFilesPromises); + + // Create a map of blame file ID to claim file ID for quick lookup + const blameToClaimMap = new Map(); + creatableBlameFiles.forEach((blameFile, index) => { + const claimFile = existingClaimFiles[index]; + if (claimFile) { + blameToClaimMap.set(blameFile._id.toString(), claimFile._id.toString()); + } + }); + + const formattedResponse = creatableBlameFiles.map((p) => { + const claimId = blameToClaimMap.get(p._id.toString()); + return { + id: p.id, + blameStatus: p.blameStatus, + requestNumber: p.requestNumber, + createdAt: new Date(p.createdAt).toLocaleString("fa-IR").split("،")[0], + timeCreatedAt: new Date(p.createdAt) + .toLocaleTimeString("fa-IR") + .split(",")[0], + ...(claimId && { claimId }), + }; + }); + + return formattedResponse; + } + + async requestDetailsWithoutStatus( + request: RequestManagementModel, + user: any, + ) { + const parties = this.determineUserPartyRole(request, user); + + request.actorsChecker = + request.actorsChecker[request.actorsChecker.length - 1]; + + return { theParties: parties, requestDetail: request }; + } + + async requestDetailsCheckedStatus( + request: any, + user: any, + ): Promise { + const finalReply = + request.expertSubmitReplyFinal || request.expertSubmitReply; + + if (!finalReply) { + throw new NotFoundException("Expert reply not found for this request."); + } + + const { guiltyUserId } = finalReply; + const isGuilty = guiltyUserId == user.sub; + + let actorDetail; + for (const a of request.actorsChecker) { + if (Object.keys(a)[0].toString() === ReqBlameStatus.CheckedRequest) { + actorDetail = a; + } + } + + let steps: ReqBlameStatus; + if (!finalReply.firstPartyComment) { + steps = ReqBlameStatus.PendingForSecondParty; + } else if (!finalReply.secondPartyComment) { + steps = ReqBlameStatus.PendingForFirstParty; + } else { + steps = ReqBlameStatus.WaitForUserAccept; + } + + await this.requestManagementDbService.findByIdAndUpdate(request._id, { + currentStep: steps, + $push: { steps: steps }, + }); + + const theParties = this.determineUserPartyRole(request, user); + + return new DetailsReplyDtoRs(request, { + actorDetail, + isGuilty, + steps, + theParties, + }); + } + + async requestDetailCloseStatus(request: RequestManagementModel) { + const finalReply = + request.expertSubmitReplyFinal || request.expertSubmitReply; + + if (!finalReply) { + return { message: "اطلاعات کارشناسی برای این پرونده یافت نشد." }; + } + + const authoritativeReply = finalReply as any; + + const bothPartiesAccepted = + authoritativeReply.firstPartyComment?.isAccept && + authoritativeReply.secondPartyComment?.isAccept; + + const wasAutoClosed = + request.autoCloseTriggerTime && + new Date(request.autoCloseTriggerTime) < new Date(); + + if (bothPartiesAccepted || wasAutoClosed) { + const guiltyUserId = authoritativeReply.guiltyUserId; + if (!guiltyUserId) { + return { message: "اطلاعات طرف مقصر در پرونده یافت نشد." }; + } + + const { firstPartyDetails, secondPartyDetails } = request; + + const isFirstPartyGuilty = + firstPartyDetails.firstPartyId.toString() === guiltyUserId.toString(); + + const guiltyUserDetail = isFirstPartyGuilty + ? [firstPartyDetails] + : [secondPartyDetails]; + const damageUserDetail = isFirstPartyGuilty + ? [secondPartyDetails] + : [firstPartyDetails]; + + return { + requestId: request["_id"], + blameStatus: request.blameStatus, + damageUserDetail, + guiltyUserDetail, + expertResendReply: request.expertResendReply, + expertSubmitReply: request.expertSubmitReply, + expertSubmitReplyFinal: request.expertSubmitReplyFinal, + }; + } else { + return { message: "یکی از طرفین پرونده را تایید نکرده است." }; + } + } + + async requestDetailsUserPendingStatus( + request: RequestManagementModel, + user: any, + ) { + const { expertResendReply } = request; + + const processPartyDetails = (partyKey: "firstParty" | "secondParty") => { + const partyReply = expertResendReply?.[partyKey]; + + if (!partyReply || user.sub !== partyReply[`${partyKey}Id`]) { + return null; + } + + const descriptionString = partyReply[`${partyKey}Description`] || ""; + const allParts = descriptionString + .split(",") + .map((p) => p.trim()) + .filter(Boolean); + + const validDocTypes = Object.values(BlameDocumentType); + const requiredDocuments: BlameDocumentType[] = []; + const descriptionTextParts: string[] = []; + + for (const part of allParts) { + const matchingDocType = validDocTypes.find( + (docType) => + docType.replace(/_/g, "").toUpperCase() === part.toUpperCase(), + ); + + if (matchingDocType) { + requiredDocuments.push(matchingDocType); + } else { + descriptionTextParts.push(part); + } + } + + const description = descriptionTextParts.join(", "); + + const otherPartyKey = + partyKey === "firstParty" ? "secondParty" : "firstParty"; + const otherPartyHasReplied = + !!expertResendReply?.[otherPartyKey]?.userReply; + + const nextStep = otherPartyHasReplied + ? "Wait For Expert Comment" + : partyKey === "firstParty" + ? "Wait For Second Party Reply" + : "Wait For First Party Reply"; + + return { + userId: partyReply[`${partyKey}Id`], + requiredDocuments, + description, + hasReplied: partyReply.userReply != null, + hasVoice: partyReply.voice != null, + nextStep: nextStep, + }; + }; + + const details = + processPartyDetails("firstParty") || processPartyDetails("secondParty"); + + return details; + } + + // TODO fix requestDetailsCheckAginStatus again + async requestDetailsCheckAgainStatus(request, user) { + const { expertResendReply } = request; + const detail = []; + + const addDetail = ( + party, + partyId, + partyDescription, + userReply, + voice, + step1, + step2, + ) => { + if (user.sub === expertResendReply?.[party]?.[`${party}Id`]) { + detail.push(expertResendReply?.[party][`${party}Id`], { + description: expertResendReply?.[party][`${party}Description`], + reply: expertResendReply[party]?.userReply !== null, + voice: !!expertResendReply[party]?.voice, + }); + if ( + expertResendReply?.[party][`${party}Id`] && + !expertResendReply?.[ + party === "firstParty" ? "secondParty" : "firstParty" + ]?.userReply + ) { + detail.push({ step: step1 }); + } else { + detail.push({ step: step2 }); + } + } + }; + + addDetail( + "firstParty", + "firstPartyId", + "firstPartyDescription", + "userReply", + "voice", + "Wait For Second Party Reply", + "Wait For Expert Comment", + ); + addDetail( + "secondParty", + "secondPartyId", + "secondPartyDescription", + "userReply", + "voice", + "Wait For First Party Reply", + "Wait For Expert Comment", + ); + + return detail; + } + + // TODO CREATE DTO FOR RESPONSE ONLY + async requestDetails(user, requestId) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) throw new NotFoundException("request not found"); + + const formattedRequest = { + ...request, + createdAt: request.createdAt, + updatedAt: request.updatedAt, + }; + + const formattingOptions: Intl.DateTimeFormatOptions = { + timeZone: "Asia/Tehran", + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + + // Check if createdAt exists and then format it on our new object. + if (request.createdAt) { + formattedRequest.createdAt = new Date(request.createdAt).toLocaleString( + "fa-IR", + formattingOptions, + ); + } + + // Do the same for updatedAt. + if (request.updatedAt) { + formattedRequest.updatedAt = new Date(request.updatedAt).toLocaleString( + "fa-IR", + formattingOptions, + ) as unknown as Date; + } + + switch (request.blameStatus) { + case ReqBlameStatus.CheckedRequest: + return await this.requestDetailsCheckedStatus(request, user); + case ReqBlameStatus.UserPending: + return await this.requestDetailsUserPendingStatus(request, user); + case ReqBlameStatus.CheckAgain: + return await this.requestDetailsCheckAgainStatus(request, user); + case ReqBlameStatus.CloseRequest: + return await this.requestDetailCloseStatus(request); + default: + // Pass the full user object, not just the username + return await this.requestDetailsWithoutStatus(request, user); + } + } + + private determineUserPartyRole( + request: RequestManagementModel, + user: any, + ): "firstParty" | "secondParty" | null { + const { firstPartyDetails, secondPartyDetails } = request; + const userId = user.sub; // The user's unique ID + const username = user.username; // The user's phone number + + // Check against both ID and phone number for robustness + if ( + userId === firstPartyDetails?.firstPartyId?.toString() || + username === firstPartyDetails?.firstPartyPhoneNumber + ) { + return "firstParty"; + } + if ( + userId === secondPartyDetails?.secondPartyId?.toString() || + username === secondPartyDetails?.secondPartyPhoneNumber + ) { + return "secondParty"; + } + return null; + } + + private async updateDamageExpertStats( + expertId: string, + requestId: string, + type: "checked" | "handled", + ) { + if (!expertId || !requestId || !["checked", "handled"].includes(type)) { + console.warn("Invalid expertId, requestId, or type"); + return; + } + + // Validate that both expertId and requestId are valid ObjectId strings + if (!Types.ObjectId.isValid(expertId)) { + console.warn("Invalid ObjectId format for expertId:", expertId); + return; + } + + if (!Types.ObjectId.isValid(requestId)) { + console.warn("Invalid ObjectId format for requestId:", requestId); + return; + } + + const expert = await this.expertDbService.findOne({ + _id: new Types.ObjectId(expertId), + }); + + if (!expert) { + console.warn("Expert not found:", expertId); + return; + } + + const requestIdStr = new Types.ObjectId(requestId).toString(); + const countedRequestIds = + expert.countedRequests?.map((id) => id.toString()) || []; + + if (type === "checked" && countedRequestIds.includes(requestIdStr)) { + console.log( + `Request ${requestIdStr} already checked for expert ${expertId}`, + ); + return; + } + + const update: any = { $inc: {}, $push: {} }; + + if (type === "checked") { + update.$inc["requestStats.totalChecked"] = 1; + update.$push["countedRequests"] = requestIdStr; + } else if (type === "handled") { + update.$inc["requestStats.totalHandled"] = 1; + + if (countedRequestIds.includes(requestIdStr)) { + update.$inc["requestStats.totalChecked"] = -1; + } + + if (!countedRequestIds.includes(requestIdStr)) { + update.$push["countedRequests"] = requestIdStr; + } else { + delete update.$push; + } + } + + const updateResult = await this.expertDbService.findOneAndUpdate( + { _id: new Types.ObjectId(expertId) }, + update, + ); + + if (!updateResult) { + console.warn("Failed to update expert stats for:", expertId); + } else { + console.log(`Expert stats updated (${type}) for expert:`, expertId); + } + } + + private async handleDisagreement( + request: any, + originalReq: any, + isFirstPartyTheDisagreer: boolean, + ): Promise { + await this.requestManagementDbService.findByIdAndUpdate(request._id, { + blameStatus: ReqBlameStatus.PartiesDisagree, + $unset: { autoCloseTriggerTime: "" }, + }); + + this.logger.log( + `Blame request ${request._id} closed with status 'PartiesDisagree'.`, + ); + + const firstPartyComment = request.expertSubmitReply?.firstPartyComment; + const secondPartyComment = request.expertSubmitReply?.secondPartyComment; + + let phoneNumberToNotify: string | null = null; + if (isFirstPartyTheDisagreer && secondPartyComment?.isAccept === true) { + phoneNumberToNotify = + originalReq.secondPartyDetails?.secondPartyPhoneNumber; + } else if ( + !isFirstPartyTheDisagreer && + firstPartyComment?.isAccept === true + ) { + phoneNumberToNotify = + originalReq.firstPartyDetails?.firstPartyPhoneNumber; + } + + // TODO SMS Sending + if (phoneNumberToNotify) { + const message = `متاسفانه طرف مقابل با نظر کارشناس مخالفت کرد. فرآیند آنلاین این پرونده بسته شده است و جهت پیگیری حضوری اقدام نمایید.`; + await this.smsManagerService.sendMessage({ + receptor: phoneNumberToNotify, + message, + }); + } + } + + private async handlePostReplyStateChanges( + updatedReq: any, + originalReq: any, + ): Promise { + const isCarBodyType = updatedReq.type === "CAR_BODY"; + + const finalReply = updatedReq.expertResendReply + ? updatedReq.expertSubmitReplyFinal + : updatedReq.expertSubmitReply; + + const firstAccept = finalReply?.firstPartyComment?.isAccept; + const secondAccept = finalReply?.secondPartyComment?.isAccept; + + // 🟥 Step 1: Handle rejections + if (!isCarBodyType && (firstAccept === false || secondAccept === false)) { + await this.handleDisagreement( + updatedReq, + originalReq, + firstAccept === false, + ); + return; + } + + // 🟦 Step 2: CAR_BODY logic (one side only) + if (isCarBodyType) { + if (firstAccept) { + await this.handleFinalStateChange(updatedReq); + } + return; + } + + // 🟩 Step 3: Two-party logic + if (updatedReq.blameStatus === ReqBlameStatus.WaitingForSignatures) { + if (firstAccept && secondAccept) { + await this.finalizeAutoResolvedRequest(updatedReq); + } else if (firstAccept || secondAccept) { + const message = `طرف مقابل توافق را امضا کرده است. لطفا جهت نهایی کردن پرونده، امضای خود را ثبت نمایید.`; + await this.handleOnePartyReplied( + updatedReq, + originalReq, + firstAccept, + message, + ); + } + return; + } + + if (firstAccept && secondAccept) { + await this.handleFinalStateChange(updatedReq); + } else if (firstAccept || secondAccept) { + const message = `طرف مقابل نظر کارشناس را قبول و امضا کرده است. در طی 24 ساعت آینده فرصت دارید تا پرونده را تکمیل کرده یا پرونده بسته خواهد شد.`; + await this.handleOnePartyReplied( + updatedReq, + originalReq, + firstAccept, + message, + ); + } + } + + private async finalizeAutoResolvedRequest(request: any): Promise { + await this.requestManagementDbService.findByIdAndUpdate(request._id, { + blameStatus: ReqBlameStatus.CloseRequest, + $unset: { autoCloseTriggerTime: "" }, + }); + + this.logger.log( + `Blame request ${request._id} auto-resolved and closed successfully.`, + ); + } + + private buildReplyUpdate( + request: any, + userId: string, + isAccept: boolean, + signDetail: any, + ): any { + const isObjection = !!request.expertResendReply; + const replyField = isObjection + ? "expertSubmitReplyFinal" + : "expertSubmitReply"; + + // Handle cases where firstPartyId might be undefined (e.g., IN_PERSON expert-filled files) + const firstPartyId = request.firstPartyDetails?.firstPartyId; + if (!firstPartyId) { + throw new BadRequestException( + "First party ID is missing. Please ensure the file has been properly initialized.", + ); + } + + const isFirstParty = userId === firstPartyId.toString(); + + const currentPartyKey = isFirstParty + ? "firstPartyComment" + : "secondPartyComment"; + const otherPartyKey = isFirstParty + ? "secondPartyComment" + : "firstPartyComment"; + + const newReplyObject = JSON.parse( + JSON.stringify(request[replyField] ?? {}), + ); + + const currentUserComment = { + isAccept: isAccept, + signDetail: { + fileId: signDetail._id, + fileName: signDetail.fileName, + }, + }; + + newReplyObject[currentPartyKey] = currentUserComment; + + if (!newReplyObject[otherPartyKey]) { + newReplyObject[otherPartyKey] = null; + } + + const update = { + $set: { + [replyField]: newReplyObject, + }, + }; + + return update; + } + + private async handleFinalStateChange(request: any): Promise { + const alreadyClosed = request.blameStatus === ReqBlameStatus.CloseRequest; + const alreadyHandled = request.isHandledStatsUpdated === true; + + if (!alreadyClosed) { + await this.requestManagementDbService.findByIdAndUpdate(request._id, { + blameStatus: ReqBlameStatus.CloseRequest, + $unset: { autoCloseTriggerTime: "" }, + }); + } + + if (!alreadyHandled) { + // Only update expert stats if the request was locked by an expert + // (CAR_BODY requests might not have actorLocked set) + if (request.actorLocked?.actorId) { + // Convert ObjectIds to strings safely + const actorIdStr = request.actorLocked.actorId.toString ? + request.actorLocked.actorId.toString() : + String(request.actorLocked.actorId); + const requestIdStr = request._id.toString ? + request._id.toString() : + String(request._id); + + await this.updateDamageExpertStats( + actorIdStr, + requestIdStr, + "handled", + ); + } + await this.requestManagementDbService.findByIdAndUpdate(request._id, { + $set: { isHandledStatsUpdated: true }, + }); + } + } + + private async handleOnePartyReplied( + updatedReq: any, + originalReq: any, + isFirstPartyAccepted: boolean, + message: string, + ): Promise { + if (updatedReq.autoCloseTriggerTime) { + return; + } + + const phoneNumber = isFirstPartyAccepted + ? originalReq.secondPartyDetails?.secondPartyPhoneNumber + : originalReq.firstPartyDetails?.firstPartyPhoneNumber; + + if (phoneNumber) { + // TODO FIX SMS SENDING FOR PARTIES + await this.smsManagerService.sendMessage({ + receptor: phoneNumber, + message, + }); + } + + await this.autoCloseRequestService.scheduleAutoClose({ + requestId: updatedReq._id.toString(), + runAt: new Date(Date.now() + 24 * 60 * 60 * 1000), + }); + } + + async userReply( + props, + isAccept: boolean, + file: Express.Multer.File, + ): Promise { + const { requestId, user } = props; + if (!file) throw new BadRequestException("A signature file is required."); + + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) throw new NotFoundException("Request not found."); + + const signDetail = await this.userSign.create({ + fileName: file.filename, + userId: user.sub, + path: file.path, + requestId, + }); + + const updatePayload = this.buildReplyUpdate( + request, + user.sub, + isAccept, + signDetail, + ); + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + const updatedReq = await this.requestManagementDbService.findOne(requestId); + + await this.handlePostReplyStateChanges(updatedReq, request); + + return new UserReplyDto(requestId); + } + + async userResend( + files: { [key: string]: Express.Multer.File[] }, + user: any, + requestId: string, + description: string, + ) { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + const partyKey = this.determinePartyAndValidate(request, user.sub); + + const { voiceId, uploadedDocumentIds } = await this.processResendUploads( + files, + requestId, + ); + + const updatePayload: any = { + $set: { + [`expertResendReply.${partyKey}.userReply`]: description, + }, + }; + + if (voiceId) { + updatePayload.$set[`expertResendReply.${partyKey}.voice`] = voiceId; + } + + for (const docType in uploadedDocumentIds) { + updatePayload.$set[`expertResendReply.${partyKey}.documents.${docType}`] = + uploadedDocumentIds[docType]; + } + + await this.requestManagementDbService.findByIdAndUpdate( + requestId, + updatePayload, + ); + + await this.updateRequestStatusIfNeeded(requestId); + + return { message: "Update saved successfully." }; + } + + // Mocked CAR_BODY insurance inquiry – replace with real external API later + private async mockCarBodyInsuranceInquiry( + requestId: string, + plate: any, + nationalCodeOfInsurer: string, + ): Promise<{ + policyNumber: string; + startDate: string; + endDate: string; + insurerCompany: string; + coverages: string[]; + }> { + this.logger.log( + `[CAR_BODY] Mocking car body insurance inquiry for request ${requestId} (plate=${JSON.stringify( + plate, + )}, nationalCodeOfInsurer=${nationalCodeOfInsurer})`, + ); + + const today = new Date(); + const oneYearLater = new Date( + today.getFullYear() + 1, + today.getMonth(), + today.getDate(), + ); + + return { + policyNumber: "CB-MOCK-123456", + startDate: today.toISOString().slice(0, 10), + endDate: oneYearLater.toISOString().slice(0, 10), + insurerCompany: "Mock Car Body Insurance Co.", + coverages: ["آتش‌سوزی", "سرقت", "بدنه کامل"], + }; + } + + private async processResendUploads( + files: { [key: string]: Express.Multer.File[] }, + requestId: string, + ) { + const uploadedDocumentIds: { [key in BlameDocumentType]?: Types.ObjectId } = + {}; + let voiceId: Types.ObjectId | null = null; + + for (const fieldName in files) { + const fileArray = files[fieldName]; + if (fileArray && fileArray.length > 0) { + const file = fileArray[0]; + + if (fieldName === "voice") { + voiceId = await this.createVoiceDocument( + file, + requestId, + UploadContext.EXPERT_RESEND, + ); + } else { + const documentType = fieldName as BlameDocumentType; + const docId = await this.createBlameDocument( + file, + requestId, + documentType, + ); + uploadedDocumentIds[documentType] = docId; + } + } + } + return { voiceId, uploadedDocumentIds }; + } + + private async createBlameDocument( + file: Express.Multer.File, + requestId: string, + documentType: BlameDocumentType, + ): Promise { + const doc = await this.blameDocumentDbService.create({ + path: file.path, + requestId: new Types.ObjectId(requestId), + fileName: file.filename, + documentType: documentType, + }); + return (doc as any)._id; + } + + private determinePartyAndValidate( + request: any, + userId: string, + ): "firstParty" | "secondParty" { + const firstPartyId = request?.firstPartyDetails?.firstPartyId?.toString(); + const secondPartyId = + request?.secondPartyDetails?.secondPartyId?.toString(); + + if (userId === firstPartyId) { + if (request.expertResendReply?.firstParty?.userReply) { + throw new BadRequestException( + "You have already submitted a reply for this resend request.", + ); + } + return "firstParty"; + } + + if (userId === secondPartyId) { + if (request.expertResendReply?.secondParty?.userReply) { + throw new BadRequestException( + "You have already submitted a reply for this resend request.", + ); + } + return "secondParty"; + } + + throw new ForbiddenException("User is not a party to this request."); + } + + private async createVoiceDocument( + file: Express.Multer.File, + requestId: string, + context: UploadContext, + ): Promise { + const voiceDoc = await this.blameVoiceDbService.create({ + path: file.path, + requestId: new Types.ObjectId(requestId), + fileName: file.filename, + context: context, + }); + return (voiceDoc as any)._id; + } + + private async updateRequestStatusIfNeeded(requestId: string): Promise { + const updatedRequest = + await this.requestManagementDbService.findOne(requestId); + const reply = updatedRequest.expertResendReply; + + if (!reply) { + return; // No resend initiated, nothing to do. + } + + // Helper function to check if a description contains a request for documents. + const doesDescriptionRequireDocuments = ( + description: string | null | undefined, + ): boolean => { + if (!description) return false; + + const validDocTypes = Object.values(BlameDocumentType); + const descriptionParts = description.split(",").map((p) => p.trim()); + + // Return true if any part of the description matches a known document type. + return descriptionParts.some((part) => + validDocTypes.some( + (docType) => + docType.replace(/_/g, "").toUpperCase() === part.toUpperCase(), + ), + ); + }; + + // 1. Determine which parties were actually asked to resubmit DOCUMENTS. + const requiredPartiesToReply: ("firstParty" | "secondParty")[] = []; + + if ( + reply.firstParty && + doesDescriptionRequireDocuments(reply.firstParty.firstPartyDescription) + ) { + requiredPartiesToReply.push("firstParty"); + } + if ( + reply.secondParty && + doesDescriptionRequireDocuments(reply.secondParty.secondPartyDescription) + ) { + requiredPartiesToReply.push("secondParty"); + } + + if (requiredPartiesToReply.length === 0) { + return; + } + + const allRequiredRepliesAreIn = requiredPartiesToReply.every( + (party) => !!reply[party]?.userReply, + ); + + if ( + allRequiredRepliesAreIn && + updatedRequest.blameStatus !== ReqBlameStatus.CheckAgain + ) { + await this.requestManagementDbService.findByIdAndUpdate(requestId, { + blameStatus: ReqBlameStatus.CheckAgain, + }); + this.logger.log( + `Request ${requestId} status updated to CheckAgain as all required parties have resubmitted.`, + ); + } + } + + private async addedClientKey(data: { clientName: any; companyCode: number }) { + if (process.env.ADDED_AUTO_CLIENT_KEY) { + let clientExist = await this.clientService.findOne({ + clientName: data.clientName, + }); + if (!clientExist) { + try { + await this.clientService.addClient({ + clientCode: data.companyCode, + clientName: { + persian: data.clientName, + english: null, + }, + property: { + smsApiKey: null, + }, + useExpertMode: "genuine", + }); + } catch (err) { + console.log(err); + } + } + } + } + + /** + * Verify that expert can access this file (only if they initiated it) + */ + private async verifyExpertAccess( + request: RequestManagementModel, + user: any, + ): Promise { + // If user is not an expert, no verification needed (normal flow) + if (user.role !== RoleEnum.EXPERT && user.role !== RoleEnum.DAMAGE_EXPERT) { + return; + } + + // If file is not expert-initiated, experts cannot access it + if (!request.expertInitiated || !request.initiatedBy) { + throw new ForbiddenException( + "Experts can only access files they have initiated", + ); + } + + // Verify the expert is the one who initiated this file + if (String(request.initiatedBy) !== user.sub) { + throw new ForbiddenException( + "You can only access files that you have initiated", + ); + } + } + + /** + * Helper method to add history event to a request + */ + private async addHistoryEvent( + requestId: string, + eventType: string, + actorId?: Types.ObjectId, + actorName?: string, + actorType?: string, + metadata?: any, + ): Promise { + const historyEvent: FileHistoryEvent = { + eventType, + actorId, + actorName, + actorType, + timestamp: new Date(), + metadata, + }; + + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + $push: { history: historyEvent }, + }, + ); + } + + /** + * Expert creates an initial blame file (incomplete, waiting for completion) + */ + async createExpertInitiatedFile( + expert: any, + dto: CreateExpertInitiatedFileDto, + ): Promise<{ requestId: string }> { + const reqNumber = new ShortUniqueId({ counter: 1 }); + const expertId = new Types.ObjectId(expert.sub); + + // For LINK method, validate phone numbers are provided + if (dto.creationMethod === CreationMethod.LINK) { + if (!dto.firstPartyPhoneNumber) { + throw new BadRequestException( + "First party phone number is required for LINK method", + ); + } + if (dto.type === "THIRD_PARTY" && !dto.secondPartyPhoneNumber) { + throw new BadRequestException( + "Second party phone number is required for LINK method with THIRD_PARTY type", + ); + } + if ( + dto.type === "THIRD_PARTY" && + dto.firstPartyPhoneNumber === dto.secondPartyPhoneNumber + ) { + throw new BadRequestException( + "First and second party phone numbers must be different", + ); + } + } + + // For LINK method, create/get users and set their IDs + let firstPartyUserId: Types.ObjectId | undefined; + let secondPartyUserId: Types.ObjectId | undefined; + let firstPartyDetails: any = {}; + let secondPartyDetails: any = {}; + + if (dto.creationMethod === CreationMethod.LINK) { + // Get or create first party user + firstPartyUserId = await this.getOrCreateUserByPhoneNumber( + dto.firstPartyPhoneNumber, + ); + firstPartyDetails = { + firstPartyId: firstPartyUserId, + firstPartyPhoneNumber: dto.firstPartyPhoneNumber, + }; + + // Get or create second party user for THIRD_PARTY + if (dto.type === "THIRD_PARTY" && dto.secondPartyPhoneNumber) { + secondPartyUserId = await this.getOrCreateUserByPhoneNumber( + dto.secondPartyPhoneNumber, + ); + secondPartyDetails = { + secondPartyId: secondPartyUserId, + secondPartyPhoneNumber: dto.secondPartyPhoneNumber, + }; + } + } + + const createRequest = await this.requestManagementDbService.create({ + requestNumber: reqNumber.rnd(), + type: dto.type, + expertInitiated: true, + initiatedBy: expertId, + creationMethod: dto.creationMethod, + filledBy: dto.creationMethod === CreationMethod.IN_PERSON ? FilledBy.EXPERT : FilledBy.CUSTOMER, + currentStep: StepsEnum.createRequest, + nextStep: + dto.type === "THIRD_PARTY" + ? StepsEnum.F_InitialForm + : StepsEnum.CarBodyForm, + blameStatus: ReqBlameStatus.PendingForFirstParty, + lockFile: false, + history: [], + assignedExpertId: expertId, // Auto-assign to initiating expert + ...(dto.creationMethod === CreationMethod.LINK && { + firstPartyDetails, + ...(dto.type === "THIRD_PARTY" && { secondPartyDetails }), + }), + }); + + const requestId = (createRequest as any)._id.toString(); + + // Add history event + await this.addHistoryEvent( + requestId, + "FILE_CREATED_BY_EXPERT", + expertId, + `${expert.firstName} ${expert.lastName}`, + "expert", + { + creationMethod: dto.creationMethod, + type: dto.type, + }, + ); + + return { + requestId, + }; + } + + /** + * Expert completes all information for IN_PERSON THIRD_PARTY file at once + * This replaces the step-by-step flow for experts filling files on-site + */ + async expertCompleteThirdPartyForm( + expert: any, + requestId: string, + formData: any, // ExpertCompleteThirdPartyFormDto + ): Promise { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // Verify this is an expert-initiated IN_PERSON file + if (!request.expertInitiated) { + throw new BadRequestException( + "This endpoint is only for expert-initiated files", + ); + } + + if (request.creationMethod !== CreationMethod.IN_PERSON) { + throw new BadRequestException( + "This endpoint is only for IN_PERSON files. LINK-based files should be completed by users step-by-step.", + ); + } + + // Verify expert access + await this.verifyExpertAccess(request, expert); + + // Validate second party is provided for THIRD_PARTY type + if (request.type === "THIRD_PARTY" && !formData.secondParty) { + throw new BadRequestException( + "Second party information is required for THIRD_PARTY type files", + ); + } + + // Validate guilty party is provided for THIRD_PARTY type + if (request.type === "THIRD_PARTY" && !formData.guiltyPartyPhoneNumber) { + throw new BadRequestException( + "Guilty party phone number is required for THIRD_PARTY type files", + ); + } + + // Validate carBodyForm is provided for CAR_BODY type + if (request.type === "CAR_BODY" && !formData.carBodyForm) { + throw new BadRequestException( + "CAR_BODY form is required for CAR_BODY type files", + ); + } + + // Validate phone numbers are different + if ( + request.type === "THIRD_PARTY" && + formData.firstPartyPhoneNumber === formData.secondParty.phoneNumber + ) { + throw new BadRequestException( + "First and second party phone numbers must be different", + ); + } + + // Validate guilty party phone number matches one of the parties + if (request.type === "THIRD_PARTY") { + const guiltyMatchesFirst = + formData.guiltyPartyPhoneNumber === formData.firstPartyPhoneNumber; + const guiltyMatchesSecond = + formData.guiltyPartyPhoneNumber === formData.secondParty.phoneNumber; + if (!guiltyMatchesFirst && !guiltyMatchesSecond) { + throw new BadRequestException( + "Guilty party phone number must match either first or second party phone number", + ); + } + } + + try { + // Get or create user for first party phone number + const firstPartyUserId = await this.getOrCreateUserByPhoneNumber( + formData.firstPartyPhoneNumber, + ); + + // Collect all steps to be added + const stepsToAdd: StepsEnum[] = [StepsEnum.F_InitialForm]; + + // Initialize firstPartyDetails structure completely + const firstPartyDetails: any = { + firstPartyId: firstPartyUserId, + firstPartyPhoneNumber: formData.firstPartyPhoneNumber, + firstPartyInitialForm: { + expertOpinion: formData.firstPartyInitialForm.expertOpinion, + imDamaged: formData.firstPartyInitialForm.imDamaged, + imGuilty: formData.firstPartyInitialForm.imGuilty, + }, + firstPartyFile: { + descriptions: [formData.firstPartyDescription.desc], + }, + }; + + // Get or create user for second party phone number + const secondPartyUserId = await this.getOrCreateUserByPhoneNumber( + formData.secondParty.phoneNumber, + ); + + const secondPartyDetails: any = { + secondPartyId: secondPartyUserId, + secondPartyPhoneNumber: formData.secondParty.phoneNumber, + secondPartyInitialForm: { + expertOpinion: formData.secondParty.initialForm.expertOpinion, + imDamaged: formData.secondParty.initialForm.imDamaged, + imGuilty: formData.secondParty.initialForm.imGuilty, + }, + secondPartyFiles: { + descriptions: [formData.secondParty.description.desc], + }, + }; + + // Build comprehensive update payload + const updatePayload: any = { + $set: { + filledBy: FilledBy.EXPERT, + firstPartyDetails: firstPartyDetails, + firstPartyLocation: formData.firstPartyLocation, + secondPartyDetails: secondPartyDetails, + secondPartyLocation: formData.secondParty.location, + }, + }; + + // Add steps for location and description + stepsToAdd.push(StepsEnum.F_addLocation); + stepsToAdd.push(StepsEnum.F_addDescription); + stepsToAdd.push(StepsEnum.S_InitialForm); + stepsToAdd.push(StepsEnum.S_addLocation); + stepsToAdd.push(StepsEnum.S_addDescription); + + // Process first party plate with SandHub lookup + const firstPartyPlate = formData.firstPartyPlate; + try { + const sandHubResponse = await this.sandHubService.getSandHubResponse({ + plate: firstPartyPlate.plate, + nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer, + }); + const sandHubReport = sandHubResponse["_doc"] || sandHubResponse; + + const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName; + const companyCode = sandHubReport?.CompanyCode; + + // Try to find client by company code first (more reliable) + const client = companyCode + ? await this.clientService.findClientWithCompanyCode(+companyCode) + : await this.clientService.findOne({ clientName: clientName }); + + if (!client) { + throw new NotFoundException( + `Client not found for company: ${clientName}`, + ); + } + + // Add first party plate and related data to the existing firstPartyDetails object + firstPartyDetails.nationalCodeOfInsurer = firstPartyPlate.nationalCodeOfInsurer; + firstPartyDetails.firstPartyPlate = firstPartyPlate.plate; + firstPartyDetails.firstPartyCarDetail = { + carName: sandHubReport?.MapTypNam || sandHubReport?.CarName, + insurerBirthday: firstPartyPlate.insurerBirthday, + driverBirthday: firstPartyPlate.driverBirthday, + carType: `${sandHubReport?.UsageField || ""} / ${sandHubReport?.MapUsageName || "-"}`, + isNewCar: firstPartyPlate.isNewCar, + }; + firstPartyDetails.firstPartyClient = { + clientId: (client as any)["_doc"]?._id || (client as any)._id, + clientName: clientName, + }; + firstPartyDetails.firstPartyCertificate = firstPartyPlate.userNoCertificate; + firstPartyDetails.firstPartyInsuranceDetail = { + insuranceId: sandHubReport?.LastCompanyDocumentNumber || sandHubReport?.DocumentNumber, + insuranceCompany: clientName, + financialCommitmentCeiling: sandHubReport?.FinancialCvrCptl || sandHubReport?.FinancialCommitment, + startDate: sandHubReport?.IssueDate || sandHubReport?.StartDate, + endDate: sandHubReport?.EndDate, + }; + + // Update the firstPartyDetails in the payload with all plate data + updatePayload.$set.firstPartyDetails = firstPartyDetails; + + stepsToAdd.push(StepsEnum.F_addPlate); + } catch (plateError) { + this.logger.error("Error processing first party plate:", plateError); + throw new InternalServerErrorException( + "Failed to process first party plate information", + ); + } + + // Process second party plate + try { + const secondPartyPlate = formData.secondParty.plate; + const sandHubResponse = await this.sandHubService.getSandHubResponse({ + plate: secondPartyPlate.plate, + nationalCodeOfInsurer: secondPartyPlate.nationalCodeOfInsurer, + }); + const sandHubReport = sandHubResponse["_doc"] || sandHubResponse; + + const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName; + const companyCode = sandHubReport?.CompanyCode; + + // Try to find client by company code first (more reliable) + const client = companyCode + ? await this.clientService.findClientWithCompanyCode(+companyCode) + : await this.clientService.findOne({ clientName: clientName }); + + if (!client) { + throw new NotFoundException( + `Client not found for company: ${clientName}${companyCode ? ` (code: ${companyCode})` : ""}`, + ); + } + + // Add second party plate and related data to the existing secondPartyDetails object + secondPartyDetails.nationalCodeOfInsurer = secondPartyPlate.nationalCodeOfInsurer; + secondPartyDetails.secondPartyPlate = secondPartyPlate.plate; + secondPartyDetails.secondPartyCarDetail = { + carName: sandHubReport?.MapTypNam || sandHubReport?.CarName, + insurerBirthday: secondPartyPlate.insurerBirthday, + driverBirthday: secondPartyPlate.driverBirthday, + carType: `${sandHubReport?.UsageField || ""} / ${sandHubReport?.MapUsageName || "-"}`, + isNewCar: secondPartyPlate.isNewCar, + }; + secondPartyDetails.secondPartyClient = { + clientId: (client as any)["_doc"]?._id || (client as any)._id, + clientName: clientName, + }; + secondPartyDetails.secondPartyCertificate = secondPartyPlate.userNoCertificate; + secondPartyDetails.secondPartyInsuranceDetail = { + insuranceId: sandHubReport?.LastCompanyDocumentNumber || sandHubReport?.DocumentNumber, + insuranceCompany: clientName, + financialCommitmentCeiling: sandHubReport?.FinancialCvrCptl || sandHubReport?.FinancialCommitment, + startDate: sandHubReport?.IssueDate || sandHubReport?.StartDate, + endDate: sandHubReport?.EndDate, + }; + + // Update the secondPartyDetails in the payload + updatePayload.$set.secondPartyDetails = secondPartyDetails; + + stepsToAdd.push(StepsEnum.S_addPlate); + } catch (plateError) { + this.logger.error("Error processing second party plate:", plateError); + throw new InternalServerErrorException( + "Failed to process second party plate information", + ); + } + + // Determine which party is guilty based on phone number + const guiltyMatchesFirst = + formData.guiltyPartyPhoneNumber === formData.firstPartyPhoneNumber; + const guiltyUserId = guiltyMatchesFirst + ? firstPartyUserId + : secondPartyDetails.secondPartyId; + + // Create initial expertSubmitReply with guiltyUserId + // Accident fields can be added separately via add-accident-fields endpoint + // This allows the file to proceed directly to signatures without expert review + updatePayload.$set.expertSubmitReply = { + description: "Expert completed all information on-site. Awaiting signatures.", + guiltyUserId: guiltyUserId, + submitTime: new Date(), + fields: { + accidentWay: { + id: "", + label: "To be determined", + }, + accidentReason: { + id: "", + label: "To be determined", + fanavaran: 0, + }, + accidentType: { + id: "", + label: "To be determined", + }, + }, + firstPartyComment: null, + secondPartyComment: null, + }; + + updatePayload.$set.currentStep = StepsEnum.S_addDescription; + updatePayload.$set.nextStep = StepsEnum.AddSignatures; + updatePayload.$set.blameStatus = ReqBlameStatus.WaitingForSignatures; + + // Add all steps at once using $each to push multiple values + if (stepsToAdd.length > 0) { + updatePayload.$push = { + steps: { $each: stepsToAdd }, + }; + } + + // Execute the update + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + // Ensure expert assignment + await this.ensureExpertAssignment(requestId); + + // Add history events + await this.addHistoryEvent( + requestId, + "EXPERT_COMPLETED_ALL_INFORMATION", + new Types.ObjectId(expert.sub), + undefined, + "expert", + { + type: "THIRD_PARTY", + hasSecondParty: true, + }, + ); + + await this.addHistoryEvent( + requestId, + "READY_FOR_SIGNATURES", + new Types.ObjectId(expert.sub), + undefined, + "system", + ); + + // Read updated request + const updatedRequest = await this.requestManagementDbService.findOne( + requestId, + ); + + return new RequestManagementDtoRs( + updatedRequest.firstPartyDetails?.firstPartyId, + updatedRequest?.secondPartyDetails?.secondPartyId || null, + StepsEnum.S_addDescription, + StepsEnum.AddSignatures, + "All information completed. Waiting for user signatures.", + updatedRequest._id.toString(), + ); + } catch (error) { + this.logger.error("Error in expertCompleteForm:", error); + if (error instanceof HttpException) { + throw error; + } + throw new InternalServerErrorException( + "Failed to complete form. Please try again.", + ); + } + } + + /** + * Expert completes all information for IN_PERSON CAR_BODY file at once + * This replaces the step-by-step flow for experts filling files on-site + */ + async expertCompleteCarBodyForm( + expert: any, + requestId: string, + formData: any, // ExpertCompleteCarBodyFormDto + ): Promise { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // Verify this is an expert-initiated IN_PERSON CAR_BODY file + if (!request.expertInitiated) { + throw new BadRequestException( + "This endpoint is only for expert-initiated files", + ); + } + + if (request.creationMethod !== CreationMethod.IN_PERSON) { + throw new BadRequestException( + "This endpoint is only for IN_PERSON files. LINK-based files should be completed by users step-by-step.", + ); + } + + if (request.type !== "CAR_BODY") { + throw new BadRequestException( + "This endpoint is only for CAR_BODY type files. Use complete-form-third-party for THIRD_PARTY files.", + ); + } + + // Verify expert access + await this.verifyExpertAccess(request, expert); + + // Validate carBodyForm is provided + if (!formData.carBodyForm) { + throw new BadRequestException( + "CAR_BODY form is required for CAR_BODY type files", + ); + } + + try { + // Get or create user for first party phone number + const firstPartyUserId = await this.getOrCreateUserByPhoneNumber( + formData.firstPartyPhoneNumber, + ); + + // Collect all steps to be added + const stepsToAdd: StepsEnum[] = [StepsEnum.CarBodyForm]; + + // Initialize firstPartyDetails structure completely + const firstPartyDetails: any = { + firstPartyId: firstPartyUserId, + firstPartyPhoneNumber: formData.firstPartyPhoneNumber, + firstPartyInitialForm: { + expertOpinion: formData.firstPartyInitialForm.expertOpinion, + imDamaged: formData.firstPartyInitialForm.imDamaged, + imGuilty: formData.firstPartyInitialForm.imGuilty, + }, + firstPartyFile: { + descriptions: [formData.firstPartyDescription.desc], + }, + carBodyForm: { + firstPartyId: firstPartyUserId, + firstPartyPhoneNumber: formData.firstPartyPhoneNumber, + carBodyForm: formData.carBodyForm, + // Assume guilty by default if accident was with another car + isGuilty: formData.carBodyForm.car ? true : false, + }, + }; + + // CAR_BODY specific fields + if (formData.firstPartyDescription.accidentDate) { + firstPartyDetails.firstPartyFile.accidentDate = formData.firstPartyDescription.accidentDate; + } + if (formData.firstPartyDescription.accidentTime) { + firstPartyDetails.firstPartyFile.accidentTime = formData.firstPartyDescription.accidentTime; + } + if (formData.firstPartyDescription.weatherCondition) { + firstPartyDetails.firstPartyFile.weatherCondition = formData.firstPartyDescription.weatherCondition; + } + if (formData.firstPartyDescription.roadCondition) { + firstPartyDetails.firstPartyFile.roadCondition = formData.firstPartyDescription.roadCondition; + } + if (formData.firstPartyDescription.lightCondition) { + firstPartyDetails.firstPartyFile.lightCondition = formData.firstPartyDescription.lightCondition; + } + + // Build comprehensive update payload + const updatePayload: any = { + $set: { + filledBy: FilledBy.EXPERT, + firstPartyDetails: firstPartyDetails, + firstPartyLocation: formData.firstPartyLocation, + }, + }; + + // Add steps for location and description + stepsToAdd.push(StepsEnum.F_addLocation); + stepsToAdd.push(StepsEnum.F_addDescription); + + // Process first party plate with SandHub lookup + const firstPartyPlate = formData.firstPartyPlate; + try { + const sandHubResponse = await this.sandHubService.getSandHubResponse({ + plate: firstPartyPlate.plate, + nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer, + }); + const sandHubReport = sandHubResponse["_doc"] || sandHubResponse; + + const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName; + const companyCode = sandHubReport?.CompanyCode; + + // Try to find client by company code first (more reliable) + const client = companyCode + ? await this.clientService.findClientWithCompanyCode(+companyCode) + : await this.clientService.findOne({ clientName: clientName }); + + if (!client) { + throw new NotFoundException( + `Client not found for company: ${clientName}`, + ); + } + + // Add first party plate and related data to the existing firstPartyDetails object + firstPartyDetails.nationalCodeOfInsurer = firstPartyPlate.nationalCodeOfInsurer; + firstPartyDetails.firstPartyPlate = firstPartyPlate.plate; + firstPartyDetails.firstPartyCarDetail = { + carName: sandHubReport?.MapTypNam || sandHubReport?.CarName, + insurerBirthday: firstPartyPlate.insurerBirthday, + driverBirthday: firstPartyPlate.driverBirthday, + carType: `${sandHubReport?.UsageField || ""} / ${sandHubReport?.MapUsageName || "-"}`, + isNewCar: firstPartyPlate.isNewCar, + }; + firstPartyDetails.firstPartyClient = { + clientId: (client as any)["_doc"]?._id || (client as any)._id, + clientName: clientName, + }; + firstPartyDetails.firstPartyCertificate = firstPartyPlate.userNoCertificate; + firstPartyDetails.firstPartyInsuranceDetail = { + insuranceId: sandHubReport?.LastCompanyDocumentNumber || sandHubReport?.DocumentNumber, + insuranceCompany: clientName, + financialCommitmentCeiling: sandHubReport?.FinancialCvrCptl || sandHubReport?.FinancialCommitment, + startDate: sandHubReport?.IssueDate || sandHubReport?.StartDate, + endDate: sandHubReport?.EndDate, + }; + + // CAR_BODY specific insurance info + const carBodyInfo = await this.mockCarBodyInsuranceInquiry( + request._id.toString(), + firstPartyPlate.plate, + firstPartyPlate.nationalCodeOfInsurer, + ); + + firstPartyDetails.firstPartyCarBodyInsuranceDetail = { + policyNumber: carBodyInfo.policyNumber, + startDate: carBodyInfo.startDate, + endDate: carBodyInfo.endDate, + insurerCompany: carBodyInfo.insurerCompany, + coverages: carBodyInfo.coverages, + }; + + // Update the firstPartyDetails in the payload with all plate data + updatePayload.$set.firstPartyDetails = firstPartyDetails; + + stepsToAdd.push(StepsEnum.F_addPlate); + } catch (plateError) { + this.logger.error("Error processing first party plate:", plateError); + throw new InternalServerErrorException( + "Failed to process first party plate information", + ); + } + + // For CAR_BODY: Create expertSubmitReply + // First party is guilty by default if accident was with another car + const guiltyUserId = firstPartyUserId; + + // Accident fields can be added separately via add-accident-fields endpoint + updatePayload.$set.expertSubmitReply = { + description: "Expert completed all information on-site. Awaiting signature.", + guiltyUserId: guiltyUserId, + submitTime: new Date(), + fields: { + accidentWay: { + id: "", + label: "To be determined", + }, + accidentReason: { + id: "", + label: "To be determined", + fanavaran: 0, + }, + accidentType: { + id: "", + label: "To be determined", + }, + }, + firstPartyComment: null, + secondPartyComment: null, + }; + + updatePayload.$set.currentStep = StepsEnum.F_addDescription; + updatePayload.$set.nextStep = StepsEnum.AddSignatures; + updatePayload.$set.blameStatus = ReqBlameStatus.WaitingForSignatures; + + // Add all steps at once using $each to push multiple values + if (stepsToAdd.length > 0) { + updatePayload.$push = { + steps: { $each: stepsToAdd }, + }; + } + + // Execute the update + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + updatePayload, + ); + + // Ensure expert assignment + await this.ensureExpertAssignment(requestId); + + // Add history events + await this.addHistoryEvent( + requestId, + "EXPERT_COMPLETED_ALL_INFORMATION", + new Types.ObjectId(expert.sub), + undefined, + "expert", + { + type: "CAR_BODY", + hasSecondParty: false, + }, + ); + + await this.addHistoryEvent( + requestId, + "READY_FOR_SIGNATURES", + new Types.ObjectId(expert.sub), + undefined, + "system", + ); + + // Read updated request + const updatedRequest = await this.requestManagementDbService.findOne( + requestId, + ); + + return new RequestManagementDtoRs( + updatedRequest.firstPartyDetails?.firstPartyId, + null, + StepsEnum.F_addDescription, + StepsEnum.AddSignatures, + "All information completed. Waiting for user signature.", + updatedRequest._id.toString(), + ); + } catch (error) { + this.logger.error("Error in expertCompleteCarBodyForm:", error); + if (error instanceof HttpException) { + throw error; + } + throw new InternalServerErrorException( + "Failed to complete form. Please try again.", + ); + } + } + + /** + * Expert uploads video for expert-initiated file + * Only one video is needed per file (not per party) + */ + async expertUploadVideo( + expert: any, + requestId: string, + file: Express.Multer.File, + ): Promise { + if (!file) { + throw new BadRequestException("Video file is required"); + } + + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // Verify this is an expert-initiated file + if (!request.expertInitiated) { + throw new BadRequestException( + "This endpoint is only for expert-initiated files", + ); + } + + // Verify expert access + await this.verifyExpertAccess(request, expert); + + // Check if video already exists + const stepToCheck = request.type === "THIRD_PARTY" ? StepsEnum.F_videoUpload : StepsEnum.CarBodyVideo; + if (request.steps?.includes(stepToCheck)) { + throw new BadRequestException("Video has already been uploaded for this file"); + } + + try { + // Create video document + const videoDocument = await this.blameVideoDbService.create({ + fileName: file.filename, + path: file.path, + requestId: new Types.ObjectId(requestId), + }); + + // Determine the step based on request type + const stepToPush = request.type === "THIRD_PARTY" ? StepsEnum.F_videoUpload : StepsEnum.CarBodyVideo; + const currentStepValue = stepToPush; + const nextStepValue = StepsEnum.F_addPlate; + + // Update request with video + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + $push: { steps: stepToPush }, + $set: { + currentStep: currentStepValue, + "firstPartyDetails.firstPartyFile.firstPartyVideoId": videoDocument["_doc"]._id, + nextStep: nextStepValue, + }, + }, + ); + + // Add history event + await this.addHistoryEvent( + requestId, + "EXPERT_UPLOADED_VIDEO", + new Types.ObjectId(expert.sub), + undefined, + "expert", + { + videoId: videoDocument["_doc"]._id.toString(), + fileName: file.filename, + }, + ); + + return new RequestManagementDtoRs( + request.firstPartyDetails?.firstPartyId || new Types.ObjectId(expert.sub), + request?.secondPartyDetails?.secondPartyId || null, + currentStepValue, + nextStepValue, + "Video uploaded successfully", + request._id, + ); + } catch (error) { + this.logger.error("Error uploading expert video:", error); + if (error instanceof HttpException) { + throw error; + } + throw new InternalServerErrorException("Failed to upload video"); + } + } + + /** + * Expert uploads voice for expert-initiated file + * Only one voice is needed per file (not per party) + */ + async expertUploadVoice( + expert: any, + requestId: string, + voice: Express.Multer.File, + ): Promise { + if (!voice) { + throw new BadRequestException("Voice file is required"); + } + + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // Verify this is an expert-initiated file + if (!request.expertInitiated) { + throw new BadRequestException( + "This endpoint is only for expert-initiated files", + ); + } + + // Verify expert access + await this.verifyExpertAccess(request, expert); + + // Check if voice already exists (check both first and second party steps) + if ( + request.steps?.includes(StepsEnum.F_addVoice) || + request.steps?.includes(StepsEnum.S_addVoice) + ) { + throw new BadRequestException("Voice has already been uploaded for this file"); + } + + try { + // Create voice document + const voiceDoc = await this.blameVoiceDbService.create({ + fileName: voice.filename, + path: voice.path, + requestId: new Types.ObjectId(requestId), + context: UploadContext.INITIAL, // Use INITIAL context for expert uploads + }); + + // Determine current step and next step based on request state + // If description is already added, next step is different + const hasDescription = request.steps?.includes(StepsEnum.F_addDescription) || + request.steps?.includes(StepsEnum.S_addDescription); + + const currentStep = request.type === "THIRD_PARTY" + ? StepsEnum.F_addVoice + : StepsEnum.F_addVoice; + + const nextStep = hasDescription + ? (request.type === "THIRD_PARTY" ? StepsEnum.AddSignatures : StepsEnum.AddSignatures) + : (request.type === "THIRD_PARTY" ? StepsEnum.F_addDescription : StepsEnum.F_addDescription); + + // Update request with voice + // For expert uploads, we store it in firstPartyFile but it represents the entire file + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + $push: { + "firstPartyDetails.firstPartyFile.voices": voiceDoc["_id"], + steps: StepsEnum.F_addVoice, + }, + $set: { + currentStep: currentStep, + nextStep: nextStep, + }, + }, + ); + + // Add history event + await this.addHistoryEvent( + requestId, + "EXPERT_UPLOADED_VOICE", + new Types.ObjectId(expert.sub), + undefined, + "expert", + { + voiceId: voiceDoc["_id"].toString(), + fileName: voice.filename, + }, + ); + + const message = hasDescription + ? "Voice uploaded successfully. File is ready for signatures." + : "Voice uploaded successfully. Please add description."; + + return new RequestManagementDtoRs( + request.firstPartyDetails?.firstPartyId || new Types.ObjectId(expert.sub), + request?.secondPartyDetails?.secondPartyId || null, + currentStep, + nextStep, + message, + request._id, + ); + } catch (error) { + this.logger.error("Error uploading expert voice:", error); + if (error instanceof HttpException) { + throw error; + } + throw new InternalServerErrorException("Failed to upload voice"); + } + } + + /** + * Expert adds accident fields to expert-initiated file + * Can be used for both IN_PERSON and LINK-based files + */ + async expertAddAccidentFields( + expert: any, + requestId: string, + fields: { accidentWay: any; accidentReason: any; accidentType: any }, + ): Promise<{ message: string; requestId: string }> { + const request = await this.requestManagementDbService.findOne(requestId); + if (!request) { + throw new NotFoundException("Request not found"); + } + + // Verify this is an expert-initiated file + if (!request.expertInitiated) { + throw new BadRequestException( + "This endpoint is only for expert-initiated files", + ); + } + + // Verify expert access + await this.verifyExpertAccess(request, expert); + + // Determine which reply field to update + const isObjection = !!request.expertResendReply; + const replyField = isObjection + ? "expertSubmitReplyFinal" + : "expertSubmitReply"; + + // Get existing reply or create new structure + const existingReply = request[replyField] || {}; + const newReplyObject: any = { + ...existingReply, + fields: { + accidentWay: fields.accidentWay, + accidentReason: fields.accidentReason, + accidentType: fields.accidentType, + }, + }; + + // Preserve other fields if they exist (for SubmitReply structure) + if (existingReply && typeof existingReply === 'object') { + if ('description' in existingReply) { + newReplyObject.description = existingReply.description; + } else { + newReplyObject.description = "Expert on-site assessment"; + } + + if ('guiltyUserId' in existingReply) { + newReplyObject.guiltyUserId = existingReply.guiltyUserId; + } else { + newReplyObject.guiltyUserId = request.firstPartyDetails?.firstPartyId; + } + + if ('submitTime' in existingReply) { + newReplyObject.submitTime = existingReply.submitTime; + } else { + newReplyObject.submitTime = new Date(); + } + + if ('firstPartyComment' in existingReply) { + newReplyObject.firstPartyComment = existingReply.firstPartyComment; + } else { + newReplyObject.firstPartyComment = null; + } + + if ('secondPartyComment' in existingReply) { + newReplyObject.secondPartyComment = existingReply.secondPartyComment; + } else { + newReplyObject.secondPartyComment = null; + } + } else { + // Create new structure if no existing reply + newReplyObject.description = "Expert on-site assessment"; + newReplyObject.guiltyUserId = request.firstPartyDetails?.firstPartyId; + newReplyObject.submitTime = new Date(); + newReplyObject.firstPartyComment = null; + newReplyObject.secondPartyComment = null; + } + + // Update the file + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + $set: { + [replyField]: newReplyObject, + }, + }, + ); + + // Add history event + await this.addHistoryEvent( + requestId, + "EXPERT_ADDED_ACCIDENT_FIELDS", + new Types.ObjectId(expert.sub), + undefined, + "expert", + { + hasObjection: isObjection, + }, + ); + + return { + message: "Accident fields added successfully", + requestId: requestId, + }; + } + + /** + * Check if request is expert-initiated and should be auto-assigned + */ + async ensureExpertAssignment(requestId: string): Promise { + const request = await this.requestManagementDbService.findOne(requestId); + + if (request?.expertInitiated && request.initiatedBy) { + // Ensure the file is assigned to the initiating expert + if ( + !request.assignedExpertId || + String(request.assignedExpertId) !== String(request.initiatedBy) + ) { + await this.requestManagementDbService.findAndUpdate( + { _id: requestId }, + { + $set: { + assignedExpertId: request.initiatedBy, + }, + }, + ); + + // Add history event + await this.addHistoryEvent( + requestId, + "AUTO_ASSIGNED_TO_EXPERT", + request.initiatedBy, + undefined, + "system", + { + expertId: request.initiatedBy.toString(), + }, + ); + } + } + } + + /** + * Get or create a user by phone number + * Used for IN_PERSON expert-initiated files where expert provides phone numbers + */ + private async getOrCreateUserByPhoneNumber(phoneNumber: string): Promise { + // Try to find existing user by phone number (username or mobile) + let user = await this.userDbService.findOne({ + $or: [ + { username: phoneNumber }, + { mobile: phoneNumber }, + ], + }); + + if (user) { + return new Types.ObjectId((user as any)._id); + } + + // User doesn't exist, create a new one + const newUser = await this.userDbService.createUser({ + mobile: phoneNumber, + username: phoneNumber, + otp: "", + tokens: { + token: "", + rfToken: "", + }, + fullName: "", + nationalCode: "", + lastLogin: new Date(), + clientKey: new Types.ObjectId(), + birthDay: "", + city: "", + address: "", + state: "", + otpExpire: 0, + }); + + return new Types.ObjectId((newUser as any)._id); + } +} diff --git a/src/sand-hub/dto/sand-hub.dto.ts b/src/sand-hub/dto/sand-hub.dto.ts new file mode 100644 index 0000000..d6f6b0b --- /dev/null +++ b/src/sand-hub/dto/sand-hub.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { PlatesDto } from "src/plates/dto/plate.dto"; +import { Plates } from "src/Types&Enums/plate.interface"; + +export class SandHubLoginDtoRs { + public loginToken: string; + constructor(token: string) { + this.loginToken = token; + } +} + +export class SandHubDetailDto { + @ApiProperty({ type: String, required: true }) + nationalCodeOfInsurer: string; + + @ApiProperty({ type: PlatesDto, required: true }) + plate: Plates; +} diff --git a/src/sand-hub/entity/db-service/sand-hub.db.service.ts b/src/sand-hub/entity/db-service/sand-hub.db.service.ts new file mode 100644 index 0000000..1e11aae --- /dev/null +++ b/src/sand-hub/entity/db-service/sand-hub.db.service.ts @@ -0,0 +1,35 @@ +import { InjectModel } from "@nestjs/mongoose"; +import { Model, Types, UpdateQuery } from "mongoose"; +import { SandHubInterface, SandHubModel } from "../schema/sand-hub.schema"; + +export class SandHubDbService { + constructor( + @InjectModel(SandHubModel.name) + private readonly sandHubModel: Model, + ) {} + + async create(sandHubData: any): Promise { + return await this.sandHubModel.create(sandHubData); + } + + async findOneByRequestId(requestId: string, userId): Promise { + return await this.sandHubModel.findOne({ requestId, userId }); + } + + async findOneBySandHubId(requestId: string): Promise { + return await this.sandHubModel.findOne({ + _id: new Types.ObjectId(requestId), + }); + } + + async findOneByRequestIdAndUpdate( + requestId: string, + userId: string, + update: UpdateQuery, + ): Promise { + return await this.sandHubModel.findOneAndUpdate( + { requestId, userId }, + update, + ); + } +} diff --git a/src/sand-hub/entity/schema/sand-hub.schema.ts b/src/sand-hub/entity/schema/sand-hub.schema.ts new file mode 100644 index 0000000..ae0e646 --- /dev/null +++ b/src/sand-hub/entity/schema/sand-hub.schema.ts @@ -0,0 +1,307 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { AddPlateDto } from "src/profile/dto/user/AddPlateDto"; + +export interface SandHubInterface { + requestId: string; + userId: string; + PrntPlcyCmpDocNo: string; + MapTypNam: string; + MtrNum: string; + ShsNum: string; + DisFnYrNum: string; + DisLfYrNum: string; + DisPrsnYrNum: string; + DisPrsnYrPrcnt: string; + DisFnYrPrcnt: string; + DisLfYrPrcnt: string; + vin: string; + MapVehicleSystemName: string; + LfCvrCptl: number; + FnCvrCptl: number; + PrsnCvrCptl: number; + VehicleSystemCode: number; + EdrsJson: string; + PersonCvrCptl: number; + LifeCvrCptl: number; + FinancialCvrCptl: number; + CarGroupCode: number; + CylCnt: number; + LastCompanyDocumentNumber: string; + UsageCode: number; + MapUsageCode: number; + MapUsageName: string; + Plk1: number; + Plk2: number; + Plk3: number; + PlkSrl: number; + PrintEndorsCompanyDocumentNumber: string; + EndorseDate: Date; + InsuranceFullName: string; + SystemField: string; + TypeField: string; + UsageField: string; + MainColorField: string; + SecondColorField: string; + ModelField: string; + CapacityField: string; + CylinderNumberField: string; + EngineNumberField: string; + ChassisNumberField: string; + VinNumberField: string; + InstallDateField: string; + AxelNumberField: string; + WheelNumberField: string; + CompanyName: string; + CompanyCode: string; + IssueDate: string; + SatrtDate: string; + EndDate: string; + Thrname: string; + EndorseText: string; + PolicyHealthLossCount: number; + PolicyFinancialLossCount: number; + PolicyPersonLossCount: number; + Tonage: number; + ThirdPolicyCode: number; + DiscountPersonPercent: number; + DiscountThirdPercent: number; + SystemCodeCii: number; + SystemNameCii: string; + TypeCodeCii: number; + TypeNameCii: string; + UsageNameCii: string; + UsageCodeCii: number; + ModelCii: number; + damageTypes: any[]; + StatusTypeCode: number; +} + +@Schema({ versionKey: false, collection: "sand-hub" }) +export class SandHubModel { + @Prop() + requestId: string; + + @Prop() + userId: string; + + @Prop() + PlateId: Types.ObjectId; + + @Prop() + PrntPlcyCmpDocNo: string; + + @Prop() + MapTypNam: string; + + @Prop() + MtrNum: string; + + @Prop() + ShsNum: string; + + @Prop() + DisFnYrNum: string; + + @Prop() + DisLfYrNum: string; + + @Prop() + DisPrsnYrNum: string; + + @Prop() + DisPrsnYrPrcnt: string; + + @Prop() + DisFnYrPrcnt: string; + + @Prop() + DisLfYrPrcnt: string; + + @Prop({ unique: false }) + vin: string; + + @Prop() + MapVehicleSystemName: string; + + @Prop() + LfCvrCptl: number; + + @Prop() + FnCvrCptl: number; + + @Prop() + PrsnCvrCptl: number; + + @Prop() + VehicleSystemCode: number; + + @Prop() + EdrsJson: string; + + @Prop() + PersonCvrCptl: number; + + @Prop() + LifeCvrCptl: number; + + @Prop() + FinancialCvrCptl: number; + + @Prop() + CarGroupCode: number; + + @Prop() + CylCnt: number; + + @Prop() + LastCompanyDocumentNumber: string; + + @Prop() + UsageCode: number; + + @Prop() + MapUsageCode: number; + + @Prop() + MapUsageName: string; + + @Prop() + Plk1: number; + + @Prop() + Plk2: number; + + @Prop() + Plk3: number; + + @Prop() + PlkSrl: number; + + @Prop() + PrintEndorsCompanyDocumentNumber: string; + + @Prop() + EndorseDate: Date; + + @Prop() + InsuranceFullName: string; + + @Prop() + SystemField: string; + + @Prop() + TypeField: string; + + @Prop() + UsageField: string; + + @Prop() + MainColorField: string; + + @Prop() + SecondColorField: string; + + @Prop() + ModelField: string; + + @Prop() + CapacityField: string; + + @Prop() + CylinderNumberField: string; + + @Prop() + EngineNumberField: string; + + @Prop() + ChassisNumberField: string; + + @Prop() + VinNumberField: string; + + @Prop() + InstallDateField: string; + + @Prop() + AxelNumberField: string; + + @Prop() + WheelNumberField: string; + + @Prop() + CompanyName: string; + + @Prop() + CompanyCode: string; + + @Prop() + IssueDate: string; + + @Prop() + SatrtDate: string; + + @Prop() + EndDate: string; + + @Prop() + Thrname: string; + + @Prop() + EndorseText: string; + + @Prop() + PolicyHealthLossCount: number; + + @Prop() + PolicyFinancialLossCount: number; + + @Prop() + PolicyPersonLossCount: number; + + @Prop() + Tonage: number; + + @Prop() + ThirdPolicyCode: number; + + @Prop() + DiscountPersonPercent: number; + + @Prop() + DiscountThirdPercent: number; + + @Prop() + SystemCodeCii: number; + + @Prop() + SystemNameCii: string; + + @Prop() + TypeCodeCii: number; + + @Prop() + TypeNameCii: string; + + @Prop() + UsageNameCii: string; + + @Prop() + UsageCodeCii: number; + + @Prop() + ModelCii: number; + + @Prop() + damageTypes: any[]; + + @Prop() + StatusTypeCode: number; + + @Prop({ type: Object }) + plate: AddPlateDto["plate"]; + + @Prop() + nationalCode: string; +} +export const SandHubSchema = SchemaFactory.createForClass(SandHubModel); diff --git a/src/sand-hub/sand-hub.module.ts b/src/sand-hub/sand-hub.module.ts new file mode 100644 index 0000000..d44393c --- /dev/null +++ b/src/sand-hub/sand-hub.module.ts @@ -0,0 +1,19 @@ +import { HttpModule } from "@nestjs/axios"; +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { SandHubDbService } from "src/sand-hub/entity/db-service/sand-hub.db.service"; +import { SandHubModel, SandHubSchema } from "./entity/schema/sand-hub.schema"; +import { SandHubService } from "./sand-hub.service"; + +@Module({ + imports: [ + HttpModule, + MongooseModule.forFeature([ + { name: SandHubModel.name, schema: SandHubSchema }, + ]), + ], + providers: [SandHubService, SandHubDbService], + exports: [SandHubService, SandHubDbService], + controllers: [], +}) +export class SandHubModule {} diff --git a/src/sand-hub/sand-hub.service.ts b/src/sand-hub/sand-hub.service.ts new file mode 100644 index 0000000..d04dc6c --- /dev/null +++ b/src/sand-hub/sand-hub.service.ts @@ -0,0 +1,380 @@ +import { HttpService } from "@nestjs/axios"; +import { + BadGatewayException, + BadRequestException, + GatewayTimeoutException, + Injectable, + Logger, + NotFoundException, + ServiceUnavailableException, + UnauthorizedException, +} from "@nestjs/common"; +import axios from "axios"; +import { SandHubDbService } from "src/sand-hub/entity/db-service/sand-hub.db.service"; +import { SandHubModel } from "src/sand-hub/entity/schema/sand-hub.schema"; +import { SandHubDetailDto } from "./dto/sand-hub.dto"; + +@Injectable() +export class SandHubService { + private readonly logger = new Logger(SandHubService.name); + private loginToken: string | null = null; + private tokenExpiry: Date | null = null; + + constructor( + private readonly httpService: HttpService, + private readonly sandHubDbService: SandHubDbService, + ) {} + + private async getAccessToken(): Promise { + if (this.loginToken && this.tokenExpiry && this.tokenExpiry > new Date()) { + return this.loginToken; + } + + this.logger.log( + "SandHub token is missing or expired. Fetching a new one...", + ); + + try { + const response = await this.httpService.axiosRef.post( + process.env.SANDHUB_URL_LOGIN, + { + email: process.env.SANDHUB_USERNAME, + password: process.env.SANDHUB_PASSWORD, + }, + ); + + const token = response.data.accessToken; + + if (!token) + throw new Error("No access token returned from SandHub login"); + + this.loginToken = token; + this.tokenExpiry = new Date(Date.now() + 55 * 60 * 1000); // 55-minute expiry + + this.logger.log("Successfully obtained a new SandHub token."); + + return this.loginToken; + } catch (er) { + this.logger.error("Failed to login to SandHub:", er.message); + this.loginToken = null; + this.tokenExpiry = null; + throw new UnauthorizedException("SandHub authentication failed"); + } + } + + async getSandHubDataFromId(sandHubId) { + return await this.sandHubDbService.findOneBySandHubId(sandHubId); + } + + private async makeSandHubRequest(url: string, payload: any, maxRetries = 3) { + const token = await this.getAccessToken(); + const INITIAL_DELAY = 1000; + const BACKOFF_FACTOR = 2; + + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + const response = await axios.post(url, payload, { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + Accept: "application/json", + }, + timeout: 50000, + }); + if (!response?.data) throw new Error("EMPTY_RESPONSE"); + return response.data; + } catch (err) { + this.handleSandHubError(err, attempt, maxRetries); + const delay = INITIAL_DELAY * Math.pow(BACKOFF_FACTOR, attempt); + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + throw new BadGatewayException( + "Failed to fetch data from SandHub after multiple retries", + ); + } + + /** + * Maps the new SandHub API response format to the old format expected by the application + */ + private mapNewApiResponseToOldFormat(newResponse: any): any { + if (!newResponse) return newResponse; + + // Map the new field names to the old field names + return { + ...newResponse, + // Company information + CompanyCode: newResponse.companyId || newResponse.CompanyCode, + CompanyName: newResponse.companyPersianName || newResponse.CompanyName, + + // Vehicle information + MapTypNam: newResponse.vehiclePersianName || newResponse.MapTypNam, + UsageField: newResponse.persianCarType || newResponse.UsageField || newResponse.usgCod, + MapUsageName: newResponse.MapUsageName || newResponse.MapUsageName, + + // Financial coverage + FinancialCvrCptl: newResponse.financeCoverage || newResponse.FinancialCvrCptl || "0", + + // Dates + IssueDate: newResponse.persianStartDate || newResponse.IssueDate, + EndDate: newResponse.persianEndDate || newResponse.EndDate, + + // Insurance details + LastCompanyDocumentNumber: newResponse.lastCompanyInsuranceNumber || newResponse.LastCompanyDocumentNumber || newResponse.insuranceNumber, + + // Technical details + MtrNum: newResponse.MtrNum || newResponse.mtrnum, + ShsNum: newResponse.ShsNum || newResponse.shsNam || newResponse.ChassisNumberField, + vin: newResponse.vin || newResponse.VinNumberField || newResponse.vin, + VinNumberField: newResponse.vin || newResponse.VinNumberField, + ChassisNumberField: newResponse.ChassisNumberField || newResponse.shsNam, + EngineNumberField: newResponse.EngineNumberField || newResponse.mtrnum, + ModelField: newResponse.ModelField || newResponse.ModelField, + + // Discount information + DisFnYrPrcnt: newResponse.DisFnYrPrcnt || newResponse.disFnYrPrcnt || "0", + DisLfYrPrcnt: newResponse.DisLfYrPrcnt || newResponse.disLfYrPrcnt || "0", + DisPrsnYrPrcnt: newResponse.DisPrsnYrPrcnt || newResponse.disPrsnYrPrcnt || "0", + DisFnYrNum: newResponse.DisFnYrNum || newResponse.disFnYrNum, + DisLfYrNum: newResponse.DisLfYrNum || newResponse.disLfYrNum, + DisPrsnYrNum: newResponse.DisPrsnYrNum || newResponse.disPrsnYrNum, + + // Usage and vehicle system codes + UsageCode: newResponse.UsageCode || newResponse.usgCod, + VehicleSystemCode: newResponse.VehicleSystemCode || newResponse.vehSysCod, + CarGroupCode: newResponse.CarGroupCode || newResponse.carGrpCod, + + // Policy information + ThirdPolicyCode: newResponse.ThirdPolicyCode || newResponse.ThirdPolicyCode, + + // Endorsement data + EdrsJson: newResponse.EdrsJson || (newResponse.edrSes ? JSON.stringify(newResponse.edrSes) : undefined), + + // Insurance fullname + InsuranceFullName: newResponse.InsuranceFullName || newResponse.fullname, + + // National code + nationalCode: newResponse.nationalCode || newResponse.ntnlId, + }; + } + + async getSandHubResponse(userDetail: SandHubDetailDto) { + try { + const requestPayload = { + leftTwoDigits: String(userDetail.plate.leftDigits), + serialLetter: String(userDetail.plate.centerAlphabet), + threeDigits: String(userDetail.plate.centerDigits), + rightTwoDigits: String(userDetail.plate.ir), + nationalCode: userDetail.nationalCodeOfInsurer, + }; + const requestUrl = `${process.env.SANDHUB_BASE_URL}/block-inquiry-tejarat`; + const response = await this.makeSandHubRequest(requestUrl, requestPayload); + + // Map the new API response format to the old format + return this.mapNewApiResponseToOldFormat(response); + } catch (err) { + throw new Error(err); + } + } + + async getPersonalInquiry(nationalCode: string, birthDate: number) { + try { + const requestUrl = `${process.env.SANDHUB_BASE_URL}/personal-inquiry/asia`; + const requestPayload = { + nin: nationalCode, + birthDate: birthDate, + persianBirthDate: "", + }; + + const response = await this.makeSandHubRequest( + requestUrl, + requestPayload, + ); + + if (response?.message?.includes("err.record.not.found")) { + throw new NotFoundException( + "Personal inquiry failed: Record not found for the given national code and birth date.", + ); + } + return response.data; + } catch (err) { + throw new Error(`Error in finding personal inquiry: ${err}`); + } + } + + async getDrivingLicenseInfo( + nationalCode: string, + driverLicenseNumber: string, + ) { + const requestUrl = `${process.env.SANDHUB_BASE_URL}/driver-license-check`; + const requestPayload = { + driverLicenseNumber, + nationalCode, + }; + + this.logger.log( + `Fetching driving license info for national code: ${nationalCode}`, + ); + + try { + const response = await this.makeSandHubRequest( + requestUrl, + requestPayload, + ); + + if (response?.data?.IsSucceed === false) { + throw new NotFoundException( + "Driving license check failed: The license is not valid or could not be found.", + ); + } + + return response.data; + } catch (error) { + if ( + error instanceof BadGatewayException && + error.message.includes("multiple retries") + ) { + throw new BadGatewayException( + `Driving license check failed after multiple retries. The service may be down.`, + ); + } + // For all other errors (like 400, 404, etc.), re-throw them as-is. + throw new Error(`Error in finding driving license: ${error}`); + } + } + + async getCarOwnershipInfo(plate: any, nationalCode: string) { + try { + const requestUrl = `${process.env.SANDHUB_BASE_URL}/ownership`; + const requestPayload = { + Plk1: String(plate.leftDigits), + Plk2: String(plate.centerAlphabet), + Plk3: String(plate.centerDigits), + plkSrl: String(plate.ir), + nationalCode: nationalCode, + }; + + this.logger.log( + `Checking car ownership for national code: ${nationalCode}`, + ); + const response = await this.makeSandHubRequest( + requestUrl, + requestPayload, + ); + + // Check the 'IsSuccess' field in the nested 'data' object. + if (response?.data?.IsSuccess === false) { + this.logger.warn( + `Car ownership check failed for national code ${nationalCode}. Response:`, + response, + ); + throw new BadRequestException( + "Ownership validation failed: The provided national ID is not the owner of this vehicle.", + ); + } + + return response; + } catch (err) { + throw new Error(`Error in finding car ownership: ${err}`); + } + } + + async getShebaValidation(nationalId: string, shebaId: string) { + try { + const requestUrl = `${process.env.SANDHUB_BASE_URL}/sheba`; + const requestPayload = { + accountOwnerType: "1", + nationalId: nationalId, + legalId: "", + shebaId: shebaId, + }; + + this.logger.log(`Validating Sheba ID for national code: ${nationalId}`); + const response = await this.makeSandHubRequest( + requestUrl, + requestPayload, + ); + + if (response?.ReturnValue === false || response?.HasError === true) { + this.logger.warn( + `Sheba validation failed for national code ${nationalId}. Response:`, + response, + ); + throw new BadRequestException( + "Sheba ID validation failed. The provided Sheba ID does not match the national ID.", + ); + } + + return response; + } catch (err) { + throw new Error(`Error in matching sheba validation: ${err}`); + } + } + + private handleSandHubError( + err: any, + attempt: number, + maxRetries: number, + ): void { + if (err.response) { + this.logger.error( + `❌ SandHub responded with status ${err.response.status} on attempt ${attempt + 1}:`, + JSON.stringify(err.response.data), + ); + + if (err.response.status === 400) { + throw new BadGatewayException( + `SandHub rejected the request with a 400 Bad Request. Details: ${JSON.stringify(err.response.data)}`, + ); + } + } else { + this.logger.error( + `❌ Network error during SandHub request (attempt ${attempt + 1}):`, + err.message, + ); + } + + if (err.message === "EMPTY_RESPONSE") { + throw new BadGatewayException( + "SandHub is offline or returned an empty response", + ); + } + if (err.code === "ECONNABORTED") { + throw new GatewayTimeoutException("SandHub request timed out"); + } + if (err.code === "ECONNRESET" || err.message.includes("socket hang up")) { + throw new ServiceUnavailableException( + "SandHub connection was reset or closed unexpectedly", + ); + } + + // This final check is for when all retries have failed for a retryable error. + if (attempt >= maxRetries) { + throw new BadGatewayException( + "Failed to fetch data from SandHub after multiple retries", + err.message, + ); + } + } + + async sandHubDocumentCreator( + userId: string, + requestId: string, + doc: Partial, + ) { + const existingDoc = await this.sandHubDbService.findOneByRequestId( + requestId, + userId, + ); + if (!existingDoc) { + return await this.sandHubDbService.create({ ...doc, userId, requestId }); + } else { + const updatePayload = { ...existingDoc, ...doc }; + return await this.sandHubDbService.findOneByRequestIdAndUpdate( + requestId, + userId, + updatePayload, + ); + } + } +} diff --git a/src/static/ACCIDENT_REASON.json b/src/static/ACCIDENT_REASON.json new file mode 100644 index 0000000..3d5cebf --- /dev/null +++ b/src/static/ACCIDENT_REASON.json @@ -0,0 +1,197 @@ +[ + { + "id": 23, + "persianLabel": "عدم رعايت فاصله جانبی", + "fanavaranID": 3 + }, + { + "id": 24, + "persianLabel": "تغيير مسير ناگهانی", + "fanavaranID": 4 + }, + { + "id": 25, + "persianLabel": "عبور از چراغ قرمز", + "fanavaranID": 5 + }, + { + "id": 26, + "persianLabel": "انحراف به راست", + "fanavaranID": 8 + }, + { + "id": 27, + "persianLabel": "حركت غيرضروربادنده عقب", + "fanavaranID": 9 + }, + { + "id": 28, + "persianLabel": "نقص فنی حادث", + "fanavaranID": 10 + }, + { + "id": 29, + "persianLabel": "سایر", + "fanavaranID": 15 + }, + { + "id": 30, + "persianLabel": "آتش سوزي بر اثر حادثه", + "fanavaranID": 18 + }, + { + "id": 31, + "persianLabel": "دور زدن در محل ممنوع", + "fanavaranID": 22 + }, + { + "id": 33, + "persianLabel": "آتشسوزي عمدي", + "fanavaranID": 11 + }, + { + "id": 34, + "persianLabel": "آتشسوزي دراثر نقص فني", + "fanavaranID": 12 + }, + { + "id": 35, + "persianLabel": "حادثه در پارک", + "fanavaranID": 19 + }, + { + "id": 36, + "persianLabel": "عدم رعايت حق تقدم", + "fanavaranID": 6 + }, + { + "id": 38, + "persianLabel": "تجاوز يا انحراف به سمت چپ معابر", + "fanavaranID": 7 + }, + { + "id": 39, + "persianLabel": "عدم توجه به جلو", + "fanavaranID": 1 + }, + { + "id": 42, + "persianLabel": "مصرف مشروبات الکلي", + "fanavaranID": 36 + }, + { + "id": 43, + "persianLabel": "عدم رعایت فاصله طولی", + "fanavaranID": 2 + }, + { + "id": 44, + "persianLabel": "خستگي وخواب آلودگي", + "fanavaranID": 16 + }, + { + "id": 45, + "persianLabel": "وقوع بلاياي طبيعي", + "fanavaranID": 14 + }, + { + "id": 50, + "persianLabel": "حرکات نمايشي و آکروباتيک", + "fanavaranID": 21 + }, + { + "id": 51, + "persianLabel": "عبور وسيله نقليه از پياده رو", + "fanavaranID": 23 + }, + { + "id": 52, + "persianLabel": "نقص سيستم روشنايي به هنگام شب", + "fanavaranID": 24 + }, + { + "id": 53, + "persianLabel": "عبور از محل ممنوع (ورود ممنوع)", + "fanavaranID": 25 + }, + { + "id": 54, + "persianLabel": "سبقت غير مجاز", + "fanavaranID": 26 + }, + { + "id": 55, + "persianLabel": "تجاوز از سرعت مجاز", + "fanavaranID": 27 + }, + { + "id": 56, + "persianLabel": "حرکت بصورت مارپيچ در راه", + "fanavaranID": 28 + }, + { + "id": 57, + "persianLabel": "گردش به چپ", + "fanavaranID": 29 + }, + { + "id": 58, + "persianLabel": "گردش به راست", + "fanavaranID": 30 + }, + { + "id": 59, + "persianLabel": "گردش به محل ممنوع", + "fanavaranID": 31 + }, + { + "id": 60, + "persianLabel": "روشن نکردن چراغ در شب در مواقع لزوم", + "fanavaranID": 32 + }, + { + "id": 61, + "persianLabel": "نقص فني مستمر", + "fanavaranID": 33 + }, + { + "id": 62, + "persianLabel": "عدم رعايت شرايط مندرج در گواهينامه", + "fanavaranID": 34 + }, + { + "id": 63, + "persianLabel": "مصرف مواد مخدر يا روان گردان", + "fanavaranID": 35 + }, + { + "id": 65, + "persianLabel": "آتش سوزي در هنگام سوخت گيري غير مجاز", + "fanavaranID": 37 + }, + { + "id": 66, + "persianLabel": "عدم توانايي در کنترل وسيله نقليه", + "fanavaranID": 38 + }, + { + "id": 67, + "persianLabel": "باز کردن ناگهاني درب خودرو", + "fanavaranID": 39 + }, + { + "id": 68, + "persianLabel": "ورود از فرعي به خيابان اصلي", + "fanavaranID": 40 + }, + { + "id": 69, + "persianLabel": "در مدارک ارائه شده علت تامه نا معلوم است", + "fanavaranID": 41 + }, + { + "id": 70, + "persianLabel": "عدم استفاده از علائم ايمني وهشداردهنده", + "fanavaranID": 42 + } +] \ No newline at end of file diff --git a/src/static/ACCIDENT_TYPE.json b/src/static/ACCIDENT_TYPE.json new file mode 100644 index 0000000..c19edfa --- /dev/null +++ b/src/static/ACCIDENT_TYPE.json @@ -0,0 +1,38 @@ +[ + { + "id": 1, + "persianLabel": "برخورد یک وسیله نقلیه با یک وسیله نقلیه" + }, + { + "id": 2, + "persianLabel": "برخورد یک وسیله نقلیه با چند وسیله نقلیه" + }, + { + "id": 3, + "persianLabel": "برخورد یک وسیله نقلیه با وسیله نقلیه پارک شده" + }, + { + "id": 4, + "persianLabel": "برخورد یک وسیله نقلیه با موتورسیکلت" + }, + { + "id": 5, + "persianLabel": "برخورد یک وسیله نقلیه با شیء ثابت" + }, + { + "id": 6, + "persianLabel": "برخورد یک وسیله نقلیه با واژگونی و سقوط" + }, + { + "id": 7, + "persianLabel": "برخورد یک وسیله نقلیه با ایجاد حریق" + }, + { + "id": 8, + "persianLabel": "برخورد یک وسیله نقلیه با حیوان وحشی" + }, + { + "id": 9, + "persianLabel": "برخورد یک وسیله نقلیه با دوچرخه" + } +] \ No newline at end of file diff --git a/src/static/ACCIDENT_WAY.json b/src/static/ACCIDENT_WAY.json new file mode 100644 index 0000000..677f46e --- /dev/null +++ b/src/static/ACCIDENT_WAY.json @@ -0,0 +1,26 @@ + [ + { + "id": 7, + "persianLabel": "جلو به جلو" + }, + { + "id": 8, + "persianLabel": "جلو به عقب" + }, + { + "id": 9, + "persianLabel": "جلو به پهلو" + }, + { + "id": 10, + "persianLabel": "پهلو به پهلو" + }, + { + "id": 11, + "persianLabel": "عقب به عقب" + }, + { + "id": 12, + "persianLabel": "سایر" + } + ] \ No newline at end of file diff --git a/src/static/car-part.json b/src/static/car-part.json new file mode 100644 index 0000000..0c393db --- /dev/null +++ b/src/static/car-part.json @@ -0,0 +1,181 @@ +{ + "قاب موتور": false, + "گلگیرهاتایرها": false, + "دیسک ترمز": false, + "لنت ترمز": false, + "روغن ترمز": false, + "ترمز دستی": false, + "پیچ چرخ‌ها": false, + "پیستون ترمز": false, + "پدال ترمز": false, + "شلنگ‌های ترمز": false, + "سوزن هواگیر": false, + "سامانه ABS": false, + "پمپ ترمز": false, + "کالیپر": false, + "بوستر": false, + "انواع سوپاپ‌های ترمز": false, + "فنرهای نگهدارنده": false, + "لنگر": false, + "کفشک ترمز": false, + "مخزن روغن ترمز": false, + "واحد تقویت‌کننده هیدرولیکی": false, + "پایه باتری": false, + "کابل باتری": false, + "سینی باتری": false, + "سامانه مدیریت باتری": false, + "دینام": false, + "ترمینال سیم‌کشی باتری": false, + "چراغ‌های جلو و عقب": false, + "چراغ‌های مه‌شکن": false, + "چراغ‌هایی داخل داشبورد": false, + "چراغ سقفی داخل کابین": false, + "حسگر دنده اتوماتیک": false, + "حسگر سرعت": false, + "حسگر دمای مایع خنک‌کننده": false, + "حسگر ترمز ABS": false, + "حسگر اکسیژن": false, + "حسگر جریان هوا": false, + "حسگر کیسه هوا": false, + "حسگر روغن": false, + "حسگر سوخت": false, + "حسگر میل‌لنگ": false, + "حسگر میل بادامک": false, + "حسگر کمربند ایمنی": false, + "حسگر نور": false, + "حسگر درها": false, + "جعبه احتراق": false, + "شمع": false, + "دلکو": false, + "کنترل‌گر زمان": false, + "سیم‌های اتصال": false, + "سوپاپ مغناطیسی": false, + "سیم‌پیچ احتراق": false, + "وایر شمع": false, + "رله فن": false, + "سوئیچ درها": false, + "سوئیچ استارت": false, + "کلید شیشه بالابر": false, + "سوئیچ قفل فرمان": false, + "ترموستات": false, + "تهویه": false, + "موتور": false, + "کف کابین": false, + "ادوات داخل کابین": false, + "اصلی": false, + "دوربین دنده عقب": false, + "دوربین‌های 360 درجه": false, + "صال به زمین": false, + "سامانه قفل مرکزی": false, + "اتصالات برقی درها": false, + "ماژول کنترل ایربگ": false, + "سامانه کنترل سرعت": false, + "سامانه مدیریت موتور": false, + "کروز کنترل": false, + "سامانه ناوبری": false, + "سوکت‌ها": false, + "سیستم قفل از راه دور": false, + "رایانه جعبه‌دنده": false, + "فیوزها": false, + "بلوک سیلندر": false, + "پوشش میل‌لنگ": false, + "پولی میل‌لنگ": false, + "میل‌لنگ": false, + "پولی پمپ آب": false, + "تسمه پروانه": false, + "پیستون": false, + "تسمه دینام": false, + "تسمه تایم": false, + "توربو شارژ": false, + "درپوش سوپاپ": false, + "دسته موتور": false, + "سرسیلندر": false, + "سوپاپ پایت": false, + "سوپاپ تهویه": false, + "شاتون": false, + "پین انگشتی": false, + "بخاری": false, + "میل بادامک": false, + "لرزش‌گیر موتور": false, + "فیلر": false, + "جعبه‌دنده": false, + "پوسته گیربکس": false, + "چرخ‌دنده‌های انتقال قدرت، جناحی، هرزگرد، سرعت‌سنج، چرخ لنگر، فرمان": false, + "پمپ دنده": false, + "دنده": false, + "اهرم تعویض دنده": false, + "دوشاخ دنده": false, + "کوپلینگ دنده": false, + "دیفرانسیل": false, + "سیلندر": false, + "فنر جعبه‌دنده": false, + "محور جعبه‌دنده": false, + "میل‌گاردان": false, + "شفت خروجی": false, + "پولوس‌ها": false, + "سامانه کلاچ": false, + "کابل تعویض دنده": false, + "فیلتر بنزین": false, + "فیلتر هوا": false, + "کاربراتور": false, + "باک سوخت": false, + "سیستم LPG": false, + "کابل ساسات": false, + "منیفولد ورودی": false, + "جداکننده آب از سوخت": false, + "پیل سوختی": false, + "سیستم CNG": false, + "پمپ‌بنزین": false, + "انژکتور": false, + "بدنه دریچه گاز": false, + "خنک‌کننده سوخت": false, + "رگلاتور": false, + "ریل": false, + "غربیلک فرمان": false, + "بازوی فرمان": false, + "جعبه فرمان": false, + "دوک": false, + "شفت فرمان": false, + "سامانه فرمان خودکار": false, + "اتصال جانبی": false, + "اتصال میل موج‌گیر": false, + "بازوی خمیده": false, + "بازوی آزاد": false, + "بازوی پیت – من": false, + "بست و اتصالات": false, + "سیبک فرمان": false, + "کمک‌فنرها": false, + "اتصالات تعادلی": false, + "فنر": false, + "محور خودرو": false, + "عایق حرارتی": false, + "گیره‌ها": false, + "لوله اگزوز": false, + "سپر حرارتی": false, + "صداخفه‌کن": false, + "رزیناتور": false, + "کاتالیست": false, + "حلقه فاصله": false, + "انواع واشرها": false, + "لوله‌های روغن": false, + "کارتل روغن": false, + "پمپ روغن": false, + "واشر پمپ روغن": false, + "صافی روغن": false, + "فیلتر روغن": false, + "تسمه فن": false, + "تیغه فن": false, + "واتر پمپ": false, + "واشر پمپ آب": false, + "دمنده هوا": false, + "بوش فن": false, + "مخزن": false, + "درپوش فشار": false, + "ترموستات": false, + "پوشش فن": false, + "کلاچ فن": false, + "پروانه یا فن": false, + "لوله‌های ورودی و خروجی آب": false, + "زانویی آب": false, + "شلنگ مایع خنک‌کننده": false +} \ No newline at end of file diff --git a/src/static/cities.json b/src/static/cities.json new file mode 100644 index 0000000..b9ca02e --- /dev/null +++ b/src/static/cities.json @@ -0,0 +1,6716 @@ +[ + { + "id": 1, + "name": "اسکو", + "slug": "اسکو", + "province_id": 1 + }, + { + "id": 2, + "name": "اهر", + "slug": "اهر", + "province_id": 1 + }, + { + "id": 3, + "name": "ایلخچی", + "slug": "ایلخچی", + "province_id": 1 + }, + { + "id": 4, + "name": "آبش احمد", + "slug": "آبش-احمد", + "province_id": 1 + }, + { + "id": 5, + "name": "آذرشهر", + "slug": "آذرشهر", + "province_id": 1 + }, + { + "id": 6, + "name": "آقکند", + "slug": "آقکند", + "province_id": 1 + }, + { + "id": 7, + "name": "باسمنج", + "slug": "باسمنج", + "province_id": 1 + }, + { + "id": 8, + "name": "بخشایش", + "slug": "بخشایش", + "province_id": 1 + }, + { + "id": 9, + "name": "بستان آباد", + "slug": "بستان-آباد", + "province_id": 1 + }, + { + "id": 10, + "name": "بناب", + "slug": "بناب", + "province_id": 1 + }, + { + "id": 11, + "name": "بناب جدید", + "slug": "بناب-جدید", + "province_id": 1 + }, + { + "id": 12, + "name": "تبریز", + "slug": "تبریز", + "province_id": 1 + }, + { + "id": 13, + "name": "ترک", + "slug": "ترک", + "province_id": 1 + }, + { + "id": 14, + "name": "ترکمانچای", + "slug": "ترکمانچای", + "province_id": 1 + }, + { + "id": 15, + "name": "تسوج", + "slug": "تسوج", + "province_id": 1 + }, + { + "id": 16, + "name": "تیکمه داش", + "slug": "تیکمه-داش", + "province_id": 1 + }, + { + "id": 17, + "name": "جلفا", + "slug": "جلفا", + "province_id": 1 + }, + { + "id": 18, + "name": "خاروانا", + "slug": "خاروانا", + "province_id": 1 + }, + { + "id": 19, + "name": "خامنه", + "slug": "خامنه", + "province_id": 1 + }, + { + "id": 20, + "name": "خراجو", + "slug": "خراجو", + "province_id": 1 + }, + { + "id": 21, + "name": "خسروشهر", + "slug": "خسروشهر", + "province_id": 1 + }, + { + "id": 22, + "name": "خضرلو", + "slug": "خضرلو", + "province_id": 1 + }, + { + "id": 23, + "name": "خمارلو", + "slug": "خمارلو", + "province_id": 1 + }, + { + "id": 24, + "name": "خواجه", + "slug": "خواجه", + "province_id": 1 + }, + { + "id": 25, + "name": "دوزدوزان", + "slug": "دوزدوزان", + "province_id": 1 + }, + { + "id": 26, + "name": "زرنق", + "slug": "زرنق", + "province_id": 1 + }, + { + "id": 27, + "name": "زنوز", + "slug": "زنوز", + "province_id": 1 + }, + { + "id": 28, + "name": "سراب", + "slug": "سراب", + "province_id": 1 + }, + { + "id": 29, + "name": "سردرود", + "slug": "سردرود", + "province_id": 1 + }, + { + "id": 30, + "name": "سهند", + "slug": "سهند", + "province_id": 1 + }, + { + "id": 31, + "name": "سیس", + "slug": "سیس", + "province_id": 1 + }, + { + "id": 32, + "name": "سیه رود", + "slug": "سیه-رود", + "province_id": 1 + }, + { + "id": 33, + "name": "شبستر", + "slug": "شبستر", + "province_id": 1 + }, + { + "id": 34, + "name": "شربیان", + "slug": "شربیان", + "province_id": 1 + }, + { + "id": 35, + "name": "شرفخانه", + "slug": "شرفخانه", + "province_id": 1 + }, + { + "id": 36, + "name": "شندآباد", + "slug": "شندآباد", + "province_id": 1 + }, + { + "id": 37, + "name": "صوفیان", + "slug": "صوفیان", + "province_id": 1 + }, + { + "id": 38, + "name": "عجب شیر", + "slug": "عجب-شیر", + "province_id": 1 + }, + { + "id": 39, + "name": "قره آغاج", + "slug": "قره-آغاج", + "province_id": 1 + }, + { + "id": 40, + "name": "کشکسرای", + "slug": "کشکسرای", + "province_id": 1 + }, + { + "id": 41, + "name": "کلوانق", + "slug": "کلوانق", + "province_id": 1 + }, + { + "id": 42, + "name": "کلیبر", + "slug": "کلیبر", + "province_id": 1 + }, + { + "id": 43, + "name": "کوزه کنان", + "slug": "کوزه-کنان", + "province_id": 1 + }, + { + "id": 44, + "name": "گوگان", + "slug": "گوگان", + "province_id": 1 + }, + { + "id": 45, + "name": "لیلان", + "slug": "لیلان", + "province_id": 1 + }, + { + "id": 46, + "name": "مراغه", + "slug": "مراغه", + "province_id": 1 + }, + { + "id": 47, + "name": "مرند", + "slug": "مرند", + "province_id": 1 + }, + { + "id": 48, + "name": "ملکان", + "slug": "ملکان", + "province_id": 1 + }, + { + "id": 49, + "name": "ملک کیان", + "slug": "ملک-کیان", + "province_id": 1 + }, + { + "id": 50, + "name": "ممقان", + "slug": "ممقان", + "province_id": 1 + }, + { + "id": 51, + "name": "مهربان", + "slug": "مهربان", + "province_id": 1 + }, + { + "id": 52, + "name": "میانه", + "slug": "میانه", + "province_id": 1 + }, + { + "id": 53, + "name": "نظرکهریزی", + "slug": "نظرکهریزی", + "province_id": 1 + }, + { + "id": 54, + "name": "هادی شهر", + "slug": "هادی-شهر", + "province_id": 1 + }, + { + "id": 55, + "name": "هرگلان", + "slug": "هرگلان", + "province_id": 1 + }, + { + "id": 56, + "name": "هریس", + "slug": "هریس", + "province_id": 1 + }, + { + "id": 57, + "name": "هشترود", + "slug": "هشترود", + "province_id": 1 + }, + { + "id": 58, + "name": "هوراند", + "slug": "هوراند", + "province_id": 1 + }, + { + "id": 59, + "name": "وایقان", + "slug": "وایقان", + "province_id": 1 + }, + { + "id": 60, + "name": "ورزقان", + "slug": "ورزقان", + "province_id": 1 + }, + { + "id": 61, + "name": "یامچی", + "slug": "یامچی", + "province_id": 1 + }, + { + "id": 62, + "name": "ارومیه", + "slug": "ارومیه", + "province_id": 2 + }, + { + "id": 63, + "name": "اشنویه", + "slug": "اشنویه", + "province_id": 2 + }, + { + "id": 64, + "name": "ایواوغلی", + "slug": "ایواوغلی", + "province_id": 2 + }, + { + "id": 65, + "name": "آواجیق", + "slug": "آواجیق", + "province_id": 2 + }, + { + "id": 66, + "name": "باروق", + "slug": "باروق", + "province_id": 2 + }, + { + "id": 67, + "name": "بازرگان", + "slug": "بازرگان", + "province_id": 2 + }, + { + "id": 68, + "name": "بوکان", + "slug": "بوکان", + "province_id": 2 + }, + { + "id": 69, + "name": "پلدشت", + "slug": "پلدشت", + "province_id": 2 + }, + { + "id": 70, + "name": "پیرانشهر", + "slug": "پیرانشهر", + "province_id": 2 + }, + { + "id": 71, + "name": "تازه شهر", + "slug": "تازه-شهر", + "province_id": 2 + }, + { + "id": 72, + "name": "تکاب", + "slug": "تکاب", + "province_id": 2 + }, + { + "id": 73, + "name": "چهاربرج", + "slug": "چهاربرج", + "province_id": 2 + }, + { + "id": 74, + "name": "خوی", + "slug": "خوی", + "province_id": 2 + }, + { + "id": 75, + "name": "دیزج دیز", + "slug": "دیزج-دیز", + "province_id": 2 + }, + { + "id": 76, + "name": "ربط", + "slug": "ربط", + "province_id": 2 + }, + { + "id": 77, + "name": "سردشت", + "slug": "آذربایجان-غربی-سردشت", + "province_id": 2 + }, + { + "id": 78, + "name": "سرو", + "slug": "سرو", + "province_id": 2 + }, + { + "id": 79, + "name": "سلماس", + "slug": "سلماس", + "province_id": 2 + }, + { + "id": 80, + "name": "سیلوانه", + "slug": "سیلوانه", + "province_id": 2 + }, + { + "id": 81, + "name": "سیمینه", + "slug": "سیمینه", + "province_id": 2 + }, + { + "id": 82, + "name": "سیه چشمه", + "slug": "سیه-چشمه", + "province_id": 2 + }, + { + "id": 83, + "name": "شاهین دژ", + "slug": "شاهین-دژ", + "province_id": 2 + }, + { + "id": 84, + "name": "شوط", + "slug": "شوط", + "province_id": 2 + }, + { + "id": 85, + "name": "فیرورق", + "slug": "فیرورق", + "province_id": 2 + }, + { + "id": 86, + "name": "قره ضیاءالدین", + "slug": "قره-ضیاءالدین", + "province_id": 2 + }, + { + "id": 87, + "name": "قطور", + "slug": "قطور", + "province_id": 2 + }, + { + "id": 88, + "name": "قوشچی", + "slug": "قوشچی", + "province_id": 2 + }, + { + "id": 89, + "name": "کشاورز", + "slug": "کشاورز", + "province_id": 2 + }, + { + "id": 90, + "name": "گردکشانه", + "slug": "گردکشانه", + "province_id": 2 + }, + { + "id": 91, + "name": "ماکو", + "slug": "ماکو", + "province_id": 2 + }, + { + "id": 92, + "name": "محمدیار", + "slug": "محمدیار", + "province_id": 2 + }, + { + "id": 93, + "name": "محمودآباد", + "slug": "آذربایجان-غربی-محمودآباد", + "province_id": 2 + }, + { + "id": 94, + "name": "مهاباد", + "slug": "آذربایجان-غربی-مهاباد", + "province_id": 2 + }, + { + "id": 95, + "name": "میاندوآب", + "slug": "میاندوآب", + "province_id": 2 + }, + { + "id": 96, + "name": "میرآباد", + "slug": "میرآباد", + "province_id": 2 + }, + { + "id": 97, + "name": "نالوس", + "slug": "نالوس", + "province_id": 2 + }, + { + "id": 98, + "name": "نقده", + "slug": "نقده", + "province_id": 2 + }, + { + "id": 99, + "name": "نوشین", + "slug": "نوشین", + "province_id": 2 + }, + { + "id": 100, + "name": "اردبیل", + "slug": "اردبیل", + "province_id": 3 + }, + { + "id": 101, + "name": "اصلاندوز", + "slug": "اصلاندوز", + "province_id": 3 + }, + { + "id": 102, + "name": "آبی بیگلو", + "slug": "آبی-بیگلو", + "province_id": 3 + }, + { + "id": 103, + "name": "بیله سوار", + "slug": "بیله-سوار", + "province_id": 3 + }, + { + "id": 104, + "name": "پارس آباد", + "slug": "پارس-آباد", + "province_id": 3 + }, + { + "id": 105, + "name": "تازه کند", + "slug": "تازه-کند", + "province_id": 3 + }, + { + "id": 106, + "name": "تازه کندانگوت", + "slug": "تازه-کندانگوت", + "province_id": 3 + }, + { + "id": 107, + "name": "جعفرآباد", + "slug": "جعفرآباد", + "province_id": 3 + }, + { + "id": 108, + "name": "خلخال", + "slug": "خلخال", + "province_id": 3 + }, + { + "id": 109, + "name": "رضی", + "slug": "رضی", + "province_id": 3 + }, + { + "id": 110, + "name": "سرعین", + "slug": "سرعین", + "province_id": 3 + }, + { + "id": 111, + "name": "عنبران", + "slug": "عنبران", + "province_id": 3 + }, + { + "id": 112, + "name": "فخرآباد", + "slug": "فخرآباد", + "province_id": 3 + }, + { + "id": 113, + "name": "کلور", + "slug": "کلور", + "province_id": 3 + }, + { + "id": 114, + "name": "کوراییم", + "slug": "کوراییم", + "province_id": 3 + }, + { + "id": 115, + "name": "گرمی", + "slug": "گرمی", + "province_id": 3 + }, + { + "id": 116, + "name": "گیوی", + "slug": "گیوی", + "province_id": 3 + }, + { + "id": 117, + "name": "لاهرود", + "slug": "لاهرود", + "province_id": 3 + }, + { + "id": 118, + "name": "مشگین شهر", + "slug": "مشگین-شهر", + "province_id": 3 + }, + { + "id": 119, + "name": "نمین", + "slug": "نمین", + "province_id": 3 + }, + { + "id": 120, + "name": "نیر", + "slug": "اردبیل-نیر", + "province_id": 3 + }, + { + "id": 121, + "name": "هشتجین", + "slug": "هشتجین", + "province_id": 3 + }, + { + "id": 122, + "name": "هیر", + "slug": "هیر", + "province_id": 3 + }, + { + "id": 123, + "name": "ابریشم", + "slug": "ابریشم", + "province_id": 4 + }, + { + "id": 124, + "name": "ابوزیدآباد", + "slug": "ابوزیدآباد", + "province_id": 4 + }, + { + "id": 125, + "name": "اردستان", + "slug": "اردستان", + "province_id": 4 + }, + { + "id": 126, + "name": "اژیه", + "slug": "اژیه", + "province_id": 4 + }, + { + "id": 127, + "name": "اصفهان", + "slug": "اصفهان", + "province_id": 4 + }, + { + "id": 128, + "name": "افوس", + "slug": "افوس", + "province_id": 4 + }, + { + "id": 129, + "name": "انارک", + "slug": "انارک", + "province_id": 4 + }, + { + "id": 130, + "name": "ایمانشهر", + "slug": "ایمانشهر", + "province_id": 4 + }, + { + "id": 131, + "name": "آران وبیدگل", + "slug": "آران-وبیدگل", + "province_id": 4 + }, + { + "id": 132, + "name": "بادرود", + "slug": "بادرود", + "province_id": 4 + }, + { + "id": 133, + "name": "باغ بهادران", + "slug": "باغ-بهادران", + "province_id": 4 + }, + { + "id": 134, + "name": "بافران", + "slug": "بافران", + "province_id": 4 + }, + { + "id": 135, + "name": "برزک", + "slug": "برزک", + "province_id": 4 + }, + { + "id": 136, + "name": "برف انبار", + "slug": "برف-انبار", + "province_id": 4 + }, + { + "id": 137, + "name": "بهاران شهر", + "slug": "بهاران-شهر", + "province_id": 4 + }, + { + "id": 138, + "name": "بهارستان", + "slug": "بهارستان", + "province_id": 4 + }, + { + "id": 139, + "name": "بوئین و میاندشت", + "slug": "بوئین-میاندشت", + "province_id": 4 + }, + { + "id": 140, + "name": "پیربکران", + "slug": "پیربکران", + "province_id": 4 + }, + { + "id": 141, + "name": "تودشک", + "slug": "تودشک", + "province_id": 4 + }, + { + "id": 142, + "name": "تیران", + "slug": "تیران", + "province_id": 4 + }, + { + "id": 143, + "name": "جندق", + "slug": "جندق", + "province_id": 4 + }, + { + "id": 144, + "name": "جوزدان", + "slug": "جوزدان", + "province_id": 4 + }, + { + "id": 145, + "name": "جوشقان و کامو", + "slug": "جوشقان-کامو", + "province_id": 4 + }, + { + "id": 146, + "name": "چادگان", + "slug": "چادگان", + "province_id": 4 + }, + { + "id": 147, + "name": "چرمهین", + "slug": "چرمهین", + "province_id": 4 + }, + { + "id": 148, + "name": "چمگردان", + "slug": "چمگردان", + "province_id": 4 + }, + { + "id": 149, + "name": "حبیب آباد", + "slug": "حبیب-آباد", + "province_id": 4 + }, + { + "id": 150, + "name": "حسن آباد", + "slug": "اصفهان-حسن-آباد", + "province_id": 4 + }, + { + "id": 151, + "name": "حنا", + "slug": "حنا", + "province_id": 4 + }, + { + "id": 152, + "name": "خالدآباد", + "slug": "خالدآباد", + "province_id": 4 + }, + { + "id": 153, + "name": "خمینی شهر", + "slug": "خمینی-شهر", + "province_id": 4 + }, + { + "id": 154, + "name": "خوانسار", + "slug": "خوانسار", + "province_id": 4 + }, + { + "id": 155, + "name": "خور", + "slug": "اصفهان-خور", + "province_id": 4 + }, + { + "id": 157, + "name": "خورزوق", + "slug": "خورزوق", + "province_id": 4 + }, + { + "id": 158, + "name": "داران", + "slug": "داران", + "province_id": 4 + }, + { + "id": 159, + "name": "دامنه", + "slug": "دامنه", + "province_id": 4 + }, + { + "id": 160, + "name": "درچه", + "slug": "درچه", + "province_id": 4 + }, + { + "id": 161, + "name": "دستگرد", + "slug": "دستگرد", + "province_id": 4 + }, + { + "id": 162, + "name": "دهاقان", + "slug": "دهاقان", + "province_id": 4 + }, + { + "id": 163, + "name": "دهق", + "slug": "دهق", + "province_id": 4 + }, + { + "id": 164, + "name": "دولت آباد", + "slug": "اصفهان-دولت-آباد", + "province_id": 4 + }, + { + "id": 165, + "name": "دیزیچه", + "slug": "دیزیچه", + "province_id": 4 + }, + { + "id": 166, + "name": "رزوه", + "slug": "رزوه", + "province_id": 4 + }, + { + "id": 167, + "name": "رضوانشهر", + "slug": "اصفهان-رضوانشهر", + "province_id": 4 + }, + { + "id": 168, + "name": "زاینده رود", + "slug": "زاینده-رود", + "province_id": 4 + }, + { + "id": 169, + "name": "زرین شهر", + "slug": "زرین-شهر", + "province_id": 4 + }, + { + "id": 170, + "name": "زواره", + "slug": "زواره", + "province_id": 4 + }, + { + "id": 171, + "name": "زیباشهر", + "slug": "زیباشهر", + "province_id": 4 + }, + { + "id": 172, + "name": "سده لنجان", + "slug": "سده-لنجان", + "province_id": 4 + }, + { + "id": 173, + "name": "سفیدشهر", + "slug": "سفیدشهر", + "province_id": 4 + }, + { + "id": 174, + "name": "سگزی", + "slug": "سگزی", + "province_id": 4 + }, + { + "id": 175, + "name": "سمیرم", + "slug": "سمیرم", + "province_id": 4 + }, + { + "id": 176, + "name": "شاهین شهر", + "slug": "شاهین-شهر", + "province_id": 4 + }, + { + "id": 177, + "name": "شهرضا", + "slug": "شهرضا", + "province_id": 4 + }, + { + "id": 178, + "name": "طالخونچه", + "slug": "طالخونچه", + "province_id": 4 + }, + { + "id": 179, + "name": "عسگران", + "slug": "عسگران", + "province_id": 4 + }, + { + "id": 180, + "name": "علویجه", + "slug": "علویجه", + "province_id": 4 + }, + { + "id": 181, + "name": "فرخی", + "slug": "فرخی", + "province_id": 4 + }, + { + "id": 182, + "name": "فریدونشهر", + "slug": "فریدونشهر", + "province_id": 4 + }, + { + "id": 183, + "name": "فلاورجان", + "slug": "فلاورجان", + "province_id": 4 + }, + { + "id": 184, + "name": "فولادشهر", + "slug": "فولادشهر", + "province_id": 4 + }, + { + "id": 185, + "name": "قمصر", + "slug": "قمصر", + "province_id": 4 + }, + { + "id": 186, + "name": "قهجاورستان", + "slug": "قهجاورستان", + "province_id": 4 + }, + { + "id": 187, + "name": "قهدریجان", + "slug": "قهدریجان", + "province_id": 4 + }, + { + "id": 188, + "name": "کاشان", + "slug": "کاشان", + "province_id": 4 + }, + { + "id": 189, + "name": "کرکوند", + "slug": "کرکوند", + "province_id": 4 + }, + { + "id": 190, + "name": "کلیشاد و سودرجان", + "slug": "کلیشاد-سودرجان", + "province_id": 4 + }, + { + "id": 191, + "name": "کمشچه", + "slug": "کمشچه", + "province_id": 4 + }, + { + "id": 192, + "name": "کمه", + "slug": "کمه", + "province_id": 4 + }, + { + "id": 193, + "name": "کهریزسنگ", + "slug": "کهریزسنگ", + "province_id": 4 + }, + { + "id": 194, + "name": "کوشک", + "slug": "کوشک", + "province_id": 4 + }, + { + "id": 195, + "name": "کوهپایه", + "slug": "کوهپایه", + "province_id": 4 + }, + { + "id": 196, + "name": "گرگاب", + "slug": "گرگاب", + "province_id": 4 + }, + { + "id": 197, + "name": "گزبرخوار", + "slug": "گزبرخوار", + "province_id": 4 + }, + { + "id": 198, + "name": "گلپایگان", + "slug": "گلپایگان", + "province_id": 4 + }, + { + "id": 199, + "name": "گلدشت", + "slug": "گلدشت", + "province_id": 4 + }, + { + "id": 200, + "name": "گلشهر", + "slug": "گلشهر", + "province_id": 4 + }, + { + "id": 201, + "name": "گوگد", + "slug": "گوگد", + "province_id": 4 + }, + { + "id": 202, + "name": "لای بید", + "slug": "لای-بید", + "province_id": 4 + }, + { + "id": 203, + "name": "مبارکه", + "slug": "مبارکه", + "province_id": 4 + }, + { + "id": 204, + "name": "مجلسی", + "slug": "مجلسی", + "province_id": 4 + }, + { + "id": 205, + "name": "محمدآباد", + "slug": "اصفهان-محمدآباد", + "province_id": 4 + }, + { + "id": 206, + "name": "مشکات", + "slug": "مشکات", + "province_id": 4 + }, + { + "id": 207, + "name": "منظریه", + "slug": "منظریه", + "province_id": 4 + }, + { + "id": 208, + "name": "مهاباد", + "slug": "اصفهان-مهاباد", + "province_id": 4 + }, + { + "id": 209, + "name": "میمه", + "slug": "اصفهان-میمه", + "province_id": 4 + }, + { + "id": 210, + "name": "نائین", + "slug": "نائین", + "province_id": 4 + }, + { + "id": 211, + "name": "نجف آباد", + "slug": "نجف-آباد", + "province_id": 4 + }, + { + "id": 212, + "name": "نصرآباد", + "slug": "اصفهان-نصرآباد", + "province_id": 4 + }, + { + "id": 213, + "name": "نطنز", + "slug": "نطنز", + "province_id": 4 + }, + { + "id": 214, + "name": "نوش آباد", + "slug": "نوش-آباد", + "province_id": 4 + }, + { + "id": 215, + "name": "نیاسر", + "slug": "نیاسر", + "province_id": 4 + }, + { + "id": 216, + "name": "نیک آباد", + "slug": "نیک-آباد", + "province_id": 4 + }, + { + "id": 217, + "name": "هرند", + "slug": "هرند", + "province_id": 4 + }, + { + "id": 218, + "name": "ورزنه", + "slug": "ورزنه", + "province_id": 4 + }, + { + "id": 219, + "name": "ورنامخواست", + "slug": "ورنامخواست", + "province_id": 4 + }, + { + "id": 220, + "name": "وزوان", + "slug": "وزوان", + "province_id": 4 + }, + { + "id": 221, + "name": "ونک", + "slug": "ونک", + "province_id": 4 + }, + { + "id": 222, + "name": "اسارا", + "slug": "اسارا", + "province_id": 5 + }, + { + "id": 223, + "name": "اشتهارد", + "slug": "اشتهارد", + "province_id": 5 + }, + { + "id": 224, + "name": "تنکمان", + "slug": "تنکمان", + "province_id": 5 + }, + { + "id": 225, + "name": "چهارباغ", + "slug": "چهارباغ", + "province_id": 5 + }, + { + "id": 226, + "name": "سعید آباد", + "slug": "سعید-آباد", + "province_id": 5 + }, + { + "id": 227, + "name": "شهر جدید هشتگرد", + "slug": "شهر-جدید-هشتگرد", + "province_id": 5 + }, + { + "id": 228, + "name": "طالقان", + "slug": "طالقان", + "province_id": 5 + }, + { + "id": 229, + "name": "کرج", + "slug": "کرج", + "province_id": 5 + }, + { + "id": 230, + "name": "کمال شهر", + "slug": "کمال-شهر", + "province_id": 5 + }, + { + "id": 231, + "name": "کوهسار", + "slug": "کوهسار", + "province_id": 5 + }, + { + "id": 232, + "name": "گرمدره", + "slug": "گرمدره", + "province_id": 5 + }, + { + "id": 233, + "name": "ماهدشت", + "slug": "ماهدشت", + "province_id": 5 + }, + { + "id": 234, + "name": "محمدشهر", + "slug": "البرز-محمدشهر", + "province_id": 5 + }, + { + "id": 235, + "name": "مشکین دشت", + "slug": "مشکین-دشت", + "province_id": 5 + }, + { + "id": 236, + "name": "نظرآباد", + "slug": "نظرآباد", + "province_id": 5 + }, + { + "id": 237, + "name": "هشتگرد", + "slug": "هشتگرد", + "province_id": 5 + }, + { + "id": 238, + "name": "ارکواز", + "slug": "ارکواز", + "province_id": 6 + }, + { + "id": 239, + "name": "ایلام", + "slug": "ایلام", + "province_id": 6 + }, + { + "id": 240, + "name": "ایوان", + "slug": "ایوان", + "province_id": 6 + }, + { + "id": 241, + "name": "آبدانان", + "slug": "آبدانان", + "province_id": 6 + }, + { + "id": 242, + "name": "آسمان آباد", + "slug": "آسمان-آباد", + "province_id": 6 + }, + { + "id": 243, + "name": "بدره", + "slug": "بدره", + "province_id": 6 + }, + { + "id": 244, + "name": "پهله", + "slug": "پهله", + "province_id": 6 + }, + { + "id": 245, + "name": "توحید", + "slug": "توحید", + "province_id": 6 + }, + { + "id": 246, + "name": "چوار", + "slug": "چوار", + "province_id": 6 + }, + { + "id": 247, + "name": "دره شهر", + "slug": "دره-شهر", + "province_id": 6 + }, + { + "id": 248, + "name": "دلگشا", + "slug": "دلگشا", + "province_id": 6 + }, + { + "id": 249, + "name": "دهلران", + "slug": "دهلران", + "province_id": 6 + }, + { + "id": 250, + "name": "زرنه", + "slug": "زرنه", + "province_id": 6 + }, + { + "id": 251, + "name": "سراب باغ", + "slug": "سراب-باغ", + "province_id": 6 + }, + { + "id": 252, + "name": "سرابله", + "slug": "سرابله", + "province_id": 6 + }, + { + "id": 253, + "name": "صالح آباد", + "slug": "ایلام-صالح-آباد", + "province_id": 6 + }, + { + "id": 254, + "name": "لومار", + "slug": "لومار", + "province_id": 6 + }, + { + "id": 255, + "name": "مهران", + "slug": "مهران", + "province_id": 6 + }, + { + "id": 256, + "name": "مورموری", + "slug": "مورموری", + "province_id": 6 + }, + { + "id": 257, + "name": "موسیان", + "slug": "موسیان", + "province_id": 6 + }, + { + "id": 258, + "name": "میمه", + "slug": "ایلام-میمه", + "province_id": 6 + }, + { + "id": 259, + "name": "امام حسن", + "slug": "امام-حسن", + "province_id": 7 + }, + { + "id": 260, + "name": "انارستان", + "slug": "انارستان", + "province_id": 7 + }, + { + "id": 261, + "name": "اهرم", + "slug": "اهرم", + "province_id": 7 + }, + { + "id": 262, + "name": "آب پخش", + "slug": "آب-پخش", + "province_id": 7 + }, + { + "id": 263, + "name": "آبدان", + "slug": "آبدان", + "province_id": 7 + }, + { + "id": 264, + "name": "برازجان", + "slug": "برازجان", + "province_id": 7 + }, + { + "id": 265, + "name": "بردخون", + "slug": "بردخون", + "province_id": 7 + }, + { + "id": 266, + "name": "بندردیر", + "slug": "بندردیر", + "province_id": 7 + }, + { + "id": 267, + "name": "بندردیلم", + "slug": "بندردیلم", + "province_id": 7 + }, + { + "id": 268, + "name": "بندرریگ", + "slug": "بندرریگ", + "province_id": 7 + }, + { + "id": 269, + "name": "بندرکنگان", + "slug": "بندرکنگان", + "province_id": 7 + }, + { + "id": 270, + "name": "بندرگناوه", + "slug": "بندرگناوه", + "province_id": 7 + }, + { + "id": 271, + "name": "بنک", + "slug": "بنک", + "province_id": 7 + }, + { + "id": 272, + "name": "بوشهر", + "slug": "بوشهر", + "province_id": 7 + }, + { + "id": 273, + "name": "تنگ ارم", + "slug": "تنگ-ارم", + "province_id": 7 + }, + { + "id": 274, + "name": "جم", + "slug": "جم", + "province_id": 7 + }, + { + "id": 275, + "name": "چغادک", + "slug": "چغادک", + "province_id": 7 + }, + { + "id": 276, + "name": "خارک", + "slug": "خارک", + "province_id": 7 + }, + { + "id": 277, + "name": "خورموج", + "slug": "خورموج", + "province_id": 7 + }, + { + "id": 278, + "name": "دالکی", + "slug": "دالکی", + "province_id": 7 + }, + { + "id": 279, + "name": "دلوار", + "slug": "دلوار", + "province_id": 7 + }, + { + "id": 280, + "name": "ریز", + "slug": "ریز", + "province_id": 7 + }, + { + "id": 281, + "name": "سعدآباد", + "slug": "سعدآباد", + "province_id": 7 + }, + { + "id": 282, + "name": "سیراف", + "slug": "سیراف", + "province_id": 7 + }, + { + "id": 283, + "name": "شبانکاره", + "slug": "شبانکاره", + "province_id": 7 + }, + { + "id": 284, + "name": "شنبه", + "slug": "شنبه", + "province_id": 7 + }, + { + "id": 285, + "name": "عسلویه", + "slug": "عسلویه", + "province_id": 7 + }, + { + "id": 286, + "name": "کاکی", + "slug": "کاکی", + "province_id": 7 + }, + { + "id": 287, + "name": "کلمه", + "slug": "کلمه", + "province_id": 7 + }, + { + "id": 288, + "name": "نخل تقی", + "slug": "نخل-تقی", + "province_id": 7 + }, + { + "id": 289, + "name": "وحدتیه", + "slug": "وحدتیه", + "province_id": 7 + }, + { + "id": 290, + "name": "ارجمند", + "slug": "ارجمند", + "province_id": 8 + }, + { + "id": 291, + "name": "اسلامشهر", + "slug": "اسلامشهر", + "province_id": 8 + }, + { + "id": 292, + "name": "اندیشه", + "slug": "اندیشه", + "province_id": 8 + }, + { + "id": 293, + "name": "آبسرد", + "slug": "آبسرد", + "province_id": 8 + }, + { + "id": 294, + "name": "آبعلی", + "slug": "آبعلی", + "province_id": 8 + }, + { + "id": 295, + "name": "باغستان", + "slug": "باغستان", + "province_id": 8 + }, + { + "id": 296, + "name": "باقرشهر", + "slug": "باقرشهر", + "province_id": 8 + }, + { + "id": 297, + "name": "بومهن", + "slug": "بومهن", + "province_id": 8 + }, + { + "id": 298, + "name": "پاکدشت", + "slug": "پاکدشت", + "province_id": 8 + }, + { + "id": 299, + "name": "پردیس", + "slug": "پردیس", + "province_id": 8 + }, + { + "id": 300, + "name": "پیشوا", + "slug": "پیشوا", + "province_id": 8 + }, + { + "id": 301, + "name": "تهران", + "slug": "تهران", + "province_id": 8 + }, + { + "id": 302, + "name": "جوادآباد", + "slug": "جوادآباد", + "province_id": 8 + }, + { + "id": 303, + "name": "چهاردانگه", + "slug": "چهاردانگه", + "province_id": 8 + }, + { + "id": 304, + "name": "حسن آباد", + "slug": "تهران-حسن-آباد", + "province_id": 8 + }, + { + "id": 305, + "name": "دماوند", + "slug": "دماوند", + "province_id": 8 + }, + { + "id": 306, + "name": "دیزین", + "slug": "دیزین", + "province_id": 8 + }, + { + "id": 307, + "name": "شهر ری", + "slug": "شهر-ری", + "province_id": 8 + }, + { + "id": 308, + "name": "رباط کریم", + "slug": "رباط-کریم", + "province_id": 8 + }, + { + "id": 309, + "name": "رودهن", + "slug": "رودهن", + "province_id": 8 + }, + { + "id": 310, + "name": "شاهدشهر", + "slug": "شاهدشهر", + "province_id": 8 + }, + { + "id": 311, + "name": "شریف آباد", + "slug": "شریف-آباد", + "province_id": 8 + }, + { + "id": 312, + "name": "شمشک", + "slug": "شمشک", + "province_id": 8 + }, + { + "id": 313, + "name": "شهریار", + "slug": "شهریار", + "province_id": 8 + }, + { + "id": 314, + "name": "صالح آباد", + "slug": "تهران-صالح-آباد", + "province_id": 8 + }, + { + "id": 315, + "name": "صباشهر", + "slug": "صباشهر", + "province_id": 8 + }, + { + "id": 316, + "name": "صفادشت", + "slug": "صفادشت", + "province_id": 8 + }, + { + "id": 317, + "name": "فردوسیه", + "slug": "فردوسیه", + "province_id": 8 + }, + { + "id": 318, + "name": "فشم", + "slug": "فشم", + "province_id": 8 + }, + { + "id": 319, + "name": "فیروزکوه", + "slug": "فیروزکوه", + "province_id": 8 + }, + { + "id": 320, + "name": "قدس", + "slug": "قدس", + "province_id": 8 + }, + { + "id": 321, + "name": "قرچک", + "slug": "قرچک", + "province_id": 8 + }, + { + "id": 322, + "name": "کهریزک", + "slug": "کهریزک", + "province_id": 8 + }, + { + "id": 323, + "name": "کیلان", + "slug": "کیلان", + "province_id": 8 + }, + { + "id": 324, + "name": "گلستان", + "slug": "شهر-گلستان", + "province_id": 8 + }, + { + "id": 325, + "name": "لواسان", + "slug": "لواسان", + "province_id": 8 + }, + { + "id": 326, + "name": "ملارد", + "slug": "ملارد", + "province_id": 8 + }, + { + "id": 327, + "name": "میگون", + "slug": "میگون", + "province_id": 8 + }, + { + "id": 328, + "name": "نسیم شهر", + "slug": "نسیم-شهر", + "province_id": 8 + }, + { + "id": 329, + "name": "نصیرآباد", + "slug": "نصیرآباد", + "province_id": 8 + }, + { + "id": 330, + "name": "وحیدیه", + "slug": "وحیدیه", + "province_id": 8 + }, + { + "id": 331, + "name": "ورامین", + "slug": "ورامین", + "province_id": 8 + }, + { + "id": 332, + "name": "اردل", + "slug": "اردل", + "province_id": 9 + }, + { + "id": 333, + "name": "آلونی", + "slug": "آلونی", + "province_id": 9 + }, + { + "id": 334, + "name": "باباحیدر", + "slug": "باباحیدر", + "province_id": 9 + }, + { + "id": 335, + "name": "بروجن", + "slug": "بروجن", + "province_id": 9 + }, + { + "id": 336, + "name": "بلداجی", + "slug": "بلداجی", + "province_id": 9 + }, + { + "id": 337, + "name": "بن", + "slug": "بن", + "province_id": 9 + }, + { + "id": 338, + "name": "جونقان", + "slug": "جونقان", + "province_id": 9 + }, + { + "id": 339, + "name": "چلگرد", + "slug": "چلگرد", + "province_id": 9 + }, + { + "id": 340, + "name": "سامان", + "slug": "سامان", + "province_id": 9 + }, + { + "id": 341, + "name": "سفیددشت", + "slug": "سفیددشت", + "province_id": 9 + }, + { + "id": 342, + "name": "سودجان", + "slug": "سودجان", + "province_id": 9 + }, + { + "id": 343, + "name": "سورشجان", + "slug": "سورشجان", + "province_id": 9 + }, + { + "id": 344, + "name": "شلمزار", + "slug": "شلمزار", + "province_id": 9 + }, + { + "id": 345, + "name": "شهرکرد", + "slug": "شهرکرد", + "province_id": 9 + }, + { + "id": 346, + "name": "طاقانک", + "slug": "طاقانک", + "province_id": 9 + }, + { + "id": 347, + "name": "فارسان", + "slug": "فارسان", + "province_id": 9 + }, + { + "id": 348, + "name": "فرادنبه", + "slug": "فرادبنه", + "province_id": 9 + }, + { + "id": 349, + "name": "فرخ شهر", + "slug": "فرخ-شهر", + "province_id": 9 + }, + { + "id": 350, + "name": "کیان", + "slug": "کیان", + "province_id": 9 + }, + { + "id": 351, + "name": "گندمان", + "slug": "گندمان", + "province_id": 9 + }, + { + "id": 352, + "name": "گهرو", + "slug": "گهرو", + "province_id": 9 + }, + { + "id": 353, + "name": "لردگان", + "slug": "لردگان", + "province_id": 9 + }, + { + "id": 354, + "name": "مال خلیفه", + "slug": "مال-خلیفه", + "province_id": 9 + }, + { + "id": 355, + "name": "ناغان", + "slug": "ناغان", + "province_id": 9 + }, + { + "id": 356, + "name": "نافچ", + "slug": "نافچ", + "province_id": 9 + }, + { + "id": 357, + "name": "نقنه", + "slug": "نقنه", + "province_id": 9 + }, + { + "id": 358, + "name": "هفشجان", + "slug": "هفشجان", + "province_id": 9 + }, + { + "id": 359, + "name": "ارسک", + "slug": "ارسک", + "province_id": 10 + }, + { + "id": 360, + "name": "اسدیه", + "slug": "اسدیه", + "province_id": 10 + }, + { + "id": 361, + "name": "اسفدن", + "slug": "اسفدن", + "province_id": 10 + }, + { + "id": 362, + "name": "اسلامیه", + "slug": "اسلامیه", + "province_id": 10 + }, + { + "id": 363, + "name": "آرین شهر", + "slug": "آرین-شهر", + "province_id": 10 + }, + { + "id": 364, + "name": "آیسک", + "slug": "آیسک", + "province_id": 10 + }, + { + "id": 365, + "name": "بشرویه", + "slug": "بشرویه", + "province_id": 10 + }, + { + "id": 366, + "name": "بیرجند", + "slug": "بیرجند", + "province_id": 10 + }, + { + "id": 367, + "name": "حاجی آباد", + "slug": "خراسان-جنوبی-حاجی-آباد", + "province_id": 10 + }, + { + "id": 368, + "name": "خضری دشت بیاض", + "slug": "خضری-دشت-بیاض", + "province_id": 10 + }, + { + "id": 369, + "name": "خوسف", + "slug": "خوسف", + "province_id": 10 + }, + { + "id": 370, + "name": "زهان", + "slug": "زهان", + "province_id": 10 + }, + { + "id": 371, + "name": "سرایان", + "slug": "سرایان", + "province_id": 10 + }, + { + "id": 372, + "name": "سربیشه", + "slug": "سربیشه", + "province_id": 10 + }, + { + "id": 373, + "name": "سه قلعه", + "slug": "سه-قلعه", + "province_id": 10 + }, + { + "id": 374, + "name": "شوسف", + "slug": "شوسف", + "province_id": 10 + }, + { + "id": 375, + "name": "طبس ", + "slug": "خراسان-جنوبی-طبس-", + "province_id": 10 + }, + { + "id": 376, + "name": "فردوس", + "slug": "فردوس", + "province_id": 10 + }, + { + "id": 377, + "name": "قاین", + "slug": "قاین", + "province_id": 10 + }, + { + "id": 378, + "name": "قهستان", + "slug": "قهستان", + "province_id": 10 + }, + { + "id": 379, + "name": "محمدشهر", + "slug": "خراسان-جنوبی-محمدشهر", + "province_id": 10 + }, + { + "id": 380, + "name": "مود", + "slug": "مود", + "province_id": 10 + }, + { + "id": 381, + "name": "نهبندان", + "slug": "نهبندان", + "province_id": 10 + }, + { + "id": 382, + "name": "نیمبلوک", + "slug": "نیمبلوک", + "province_id": 10 + }, + { + "id": 383, + "name": "احمدآباد صولت", + "slug": "احمدآباد-صولت", + "province_id": 11 + }, + { + "id": 384, + "name": "انابد", + "slug": "انابد", + "province_id": 11 + }, + { + "id": 385, + "name": "باجگیران", + "slug": "باجگیران", + "province_id": 11 + }, + { + "id": 386, + "name": "باخرز", + "slug": "باخرز", + "province_id": 11 + }, + { + "id": 387, + "name": "بار", + "slug": "بار", + "province_id": 11 + }, + { + "id": 388, + "name": "بایگ", + "slug": "بایگ", + "province_id": 11 + }, + { + "id": 389, + "name": "بجستان", + "slug": "بجستان", + "province_id": 11 + }, + { + "id": 390, + "name": "بردسکن", + "slug": "بردسکن", + "province_id": 11 + }, + { + "id": 391, + "name": "بیدخت", + "slug": "بیدخت", + "province_id": 11 + }, + { + "id": 392, + "name": "تایباد", + "slug": "تایباد", + "province_id": 11 + }, + { + "id": 393, + "name": "تربت جام", + "slug": "تربت-جام", + "province_id": 11 + }, + { + "id": 394, + "name": "تربت حیدریه", + "slug": "تربت-حیدریه", + "province_id": 11 + }, + { + "id": 395, + "name": "جغتای", + "slug": "جغتای", + "province_id": 11 + }, + { + "id": 396, + "name": "جنگل", + "slug": "جنگل", + "province_id": 11 + }, + { + "id": 397, + "name": "چاپشلو", + "slug": "چاپشلو", + "province_id": 11 + }, + { + "id": 398, + "name": "چکنه", + "slug": "چکنه", + "province_id": 11 + }, + { + "id": 399, + "name": "چناران", + "slug": "چناران", + "province_id": 11 + }, + { + "id": 400, + "name": "خرو", + "slug": "خرو", + "province_id": 11 + }, + { + "id": 401, + "name": "خلیل آباد", + "slug": "خلیل-آباد", + "province_id": 11 + }, + { + "id": 402, + "name": "خواف", + "slug": "خواف", + "province_id": 11 + }, + { + "id": 403, + "name": "داورزن", + "slug": "داورزن", + "province_id": 11 + }, + { + "id": 404, + "name": "درگز", + "slug": "درگز", + "province_id": 11 + }, + { + "id": 405, + "name": "در رود", + "slug": "در-رود", + "province_id": 11 + }, + { + "id": 406, + "name": "دولت آباد", + "slug": "خراسان-رضوی-دولت-آباد", + "province_id": 11 + }, + { + "id": 407, + "name": "رباط سنگ", + "slug": "رباط-سنگ", + "province_id": 11 + }, + { + "id": 408, + "name": "رشتخوار", + "slug": "رشتخوار", + "province_id": 11 + }, + { + "id": 409, + "name": "رضویه", + "slug": "رضویه", + "province_id": 11 + }, + { + "id": 410, + "name": "روداب", + "slug": "روداب", + "province_id": 11 + }, + { + "id": 411, + "name": "ریوش", + "slug": "ریوش", + "province_id": 11 + }, + { + "id": 412, + "name": "سبزوار", + "slug": "سبزوار", + "province_id": 11 + }, + { + "id": 413, + "name": "سرخس", + "slug": "سرخس", + "province_id": 11 + }, + { + "id": 414, + "name": "سفیدسنگ", + "slug": "سفیدسنگ", + "province_id": 11 + }, + { + "id": 415, + "name": "سلامی", + "slug": "سلامی", + "province_id": 11 + }, + { + "id": 416, + "name": "سلطان آباد", + "slug": "سلطان-آباد", + "province_id": 11 + }, + { + "id": 417, + "name": "سنگان", + "slug": "سنگان", + "province_id": 11 + }, + { + "id": 418, + "name": "شادمهر", + "slug": "شادمهر", + "province_id": 11 + }, + { + "id": 419, + "name": "شاندیز", + "slug": "شاندیز", + "province_id": 11 + }, + { + "id": 420, + "name": "ششتمد", + "slug": "ششتمد", + "province_id": 11 + }, + { + "id": 421, + "name": "شهرآباد", + "slug": "شهرآباد", + "province_id": 11 + }, + { + "id": 422, + "name": "شهرزو", + "slug": "شهرزو", + "province_id": 11 + }, + { + "id": 423, + "name": "صالح آباد", + "slug": "خراسان-رضوی-صالح-آباد", + "province_id": 11 + }, + { + "id": 424, + "name": "طرقبه", + "slug": "طرقبه", + "province_id": 11 + }, + { + "id": 425, + "name": "عشق آباد", + "slug": "خراسان-رضوی-عشق-آباد", + "province_id": 11 + }, + { + "id": 426, + "name": "فرهادگرد", + "slug": "فرهادگرد", + "province_id": 11 + }, + { + "id": 427, + "name": "فریمان", + "slug": "فریمان", + "province_id": 11 + }, + { + "id": 428, + "name": "فیروزه", + "slug": "فیروزه", + "province_id": 11 + }, + { + "id": 429, + "name": "فیض آباد", + "slug": "فیض-آباد", + "province_id": 11 + }, + { + "id": 430, + "name": "قاسم آباد", + "slug": "قاسم-آباد", + "province_id": 11 + }, + { + "id": 431, + "name": "قدمگاه", + "slug": "قدمگاه", + "province_id": 11 + }, + { + "id": 432, + "name": "قلندرآباد", + "slug": "قلندرآباد", + "province_id": 11 + }, + { + "id": 433, + "name": "قوچان", + "slug": "قوچان", + "province_id": 11 + }, + { + "id": 434, + "name": "کاخک", + "slug": "کاخک", + "province_id": 11 + }, + { + "id": 435, + "name": "کاریز", + "slug": "کاریز", + "province_id": 11 + }, + { + "id": 436, + "name": "کاشمر", + "slug": "کاشمر", + "province_id": 11 + }, + { + "id": 437, + "name": "کدکن", + "slug": "کدکن", + "province_id": 11 + }, + { + "id": 438, + "name": "کلات", + "slug": "کلات", + "province_id": 11 + }, + { + "id": 439, + "name": "کندر", + "slug": "کندر", + "province_id": 11 + }, + { + "id": 440, + "name": "گلمکان", + "slug": "گلمکان", + "province_id": 11 + }, + { + "id": 441, + "name": "گناباد", + "slug": "گناباد", + "province_id": 11 + }, + { + "id": 442, + "name": "لطف آباد", + "slug": "لطف-آباد", + "province_id": 11 + }, + { + "id": 443, + "name": "مزدآوند", + "slug": "مزدآوند", + "province_id": 11 + }, + { + "id": 444, + "name": "مشهد", + "slug": "مشهد", + "province_id": 11 + }, + { + "id": 445, + "name": "ملک آباد", + "slug": "ملک-آباد", + "province_id": 11 + }, + { + "id": 446, + "name": "نشتیفان", + "slug": "نشتیفان", + "province_id": 11 + }, + { + "id": 447, + "name": "نصرآباد", + "slug": "خراسان-رضوی-نصرآباد", + "province_id": 11 + }, + { + "id": 448, + "name": "نقاب", + "slug": "نقاب", + "province_id": 11 + }, + { + "id": 449, + "name": "نوخندان", + "slug": "نوخندان", + "province_id": 11 + }, + { + "id": 450, + "name": "نیشابور", + "slug": "نیشابور", + "province_id": 11 + }, + { + "id": 451, + "name": "نیل شهر", + "slug": "نیل-شهر", + "province_id": 11 + }, + { + "id": 452, + "name": "همت آباد", + "slug": "همت-آباد", + "province_id": 11 + }, + { + "id": 453, + "name": "یونسی", + "slug": "یونسی", + "province_id": 11 + }, + { + "id": 454, + "name": "اسفراین", + "slug": "اسفراین", + "province_id": 12 + }, + { + "id": 455, + "name": "ایور", + "slug": "ایور", + "province_id": 12 + }, + { + "id": 456, + "name": "آشخانه", + "slug": "آشخانه", + "province_id": 12 + }, + { + "id": 457, + "name": "بجنورد", + "slug": "بجنورد", + "province_id": 12 + }, + { + "id": 458, + "name": "پیش قلعه", + "slug": "پیش-قلعه", + "province_id": 12 + }, + { + "id": 459, + "name": "تیتکانلو", + "slug": "تیتکانلو", + "province_id": 12 + }, + { + "id": 460, + "name": "جاجرم", + "slug": "جاجرم", + "province_id": 12 + }, + { + "id": 461, + "name": "حصارگرمخان", + "slug": "حصارگرمخان", + "province_id": 12 + }, + { + "id": 462, + "name": "درق", + "slug": "درق", + "province_id": 12 + }, + { + "id": 463, + "name": "راز", + "slug": "راز", + "province_id": 12 + }, + { + "id": 464, + "name": "سنخواست", + "slug": "سنخواست", + "province_id": 12 + }, + { + "id": 465, + "name": "شوقان", + "slug": "شوقان", + "province_id": 12 + }, + { + "id": 466, + "name": "شیروان", + "slug": "شیروان", + "province_id": 12 + }, + { + "id": 467, + "name": "صفی آباد", + "slug": "خراسان-شمالی-صفی-آباد", + "province_id": 12 + }, + { + "id": 468, + "name": "فاروج", + "slug": "فاروج", + "province_id": 12 + }, + { + "id": 469, + "name": "قاضی", + "slug": "قاضی", + "province_id": 12 + }, + { + "id": 470, + "name": "گرمه", + "slug": "گرمه", + "province_id": 12 + }, + { + "id": 471, + "name": "لوجلی", + "slug": "لوجلی", + "province_id": 12 + }, + { + "id": 472, + "name": "اروندکنار", + "slug": "اروندکنار", + "province_id": 13 + }, + { + "id": 473, + "name": "الوان", + "slug": "الوان", + "province_id": 13 + }, + { + "id": 474, + "name": "امیدیه", + "slug": "امیدیه", + "province_id": 13 + }, + { + "id": 475, + "name": "اندیمشک", + "slug": "اندیمشک", + "province_id": 13 + }, + { + "id": 476, + "name": "اهواز", + "slug": "اهواز", + "province_id": 13 + }, + { + "id": 477, + "name": "ایذه", + "slug": "ایذه", + "province_id": 13 + }, + { + "id": 478, + "name": "آبادان", + "slug": "آبادان", + "province_id": 13 + }, + { + "id": 479, + "name": "آغاجاری", + "slug": "آغاجاری", + "province_id": 13 + }, + { + "id": 480, + "name": "باغ ملک", + "slug": "باغ-ملک", + "province_id": 13 + }, + { + "id": 481, + "name": "بستان", + "slug": "بستان", + "province_id": 13 + }, + { + "id": 482, + "name": "بندرامام خمینی", + "slug": "بندرامام-خمینی", + "province_id": 13 + }, + { + "id": 483, + "name": "بندرماهشهر", + "slug": "بندرماهشهر", + "province_id": 13 + }, + { + "id": 484, + "name": "بهبهان", + "slug": "بهبهان", + "province_id": 13 + }, + { + "id": 485, + "name": "ترکالکی", + "slug": "ترکالکی", + "province_id": 13 + }, + { + "id": 486, + "name": "جایزان", + "slug": "جایزان", + "province_id": 13 + }, + { + "id": 487, + "name": "چمران", + "slug": "چمران", + "province_id": 13 + }, + { + "id": 488, + "name": "چویبده", + "slug": "چویبده", + "province_id": 13 + }, + { + "id": 489, + "name": "حر", + "slug": "حر", + "province_id": 13 + }, + { + "id": 490, + "name": "حسینیه", + "slug": "حسینیه", + "province_id": 13 + }, + { + "id": 491, + "name": "حمزه", + "slug": "حمزه", + "province_id": 13 + }, + { + "id": 492, + "name": "حمیدیه", + "slug": "حمیدیه", + "province_id": 13 + }, + { + "id": 493, + "name": "خرمشهر", + "slug": "خرمشهر", + "province_id": 13 + }, + { + "id": 494, + "name": "دارخوین", + "slug": "دارخوین", + "province_id": 13 + }, + { + "id": 495, + "name": "دزآب", + "slug": "دزآب", + "province_id": 13 + }, + { + "id": 496, + "name": "دزفول", + "slug": "دزفول", + "province_id": 13 + }, + { + "id": 497, + "name": "دهدز", + "slug": "دهدز", + "province_id": 13 + }, + { + "id": 498, + "name": "رامشیر", + "slug": "رامشیر", + "province_id": 13 + }, + { + "id": 499, + "name": "رامهرمز", + "slug": "رامهرمز", + "province_id": 13 + }, + { + "id": 500, + "name": "رفیع", + "slug": "رفیع", + "province_id": 13 + }, + { + "id": 501, + "name": "زهره", + "slug": "زهره", + "province_id": 13 + }, + { + "id": 502, + "name": "سالند", + "slug": "سالند", + "province_id": 13 + }, + { + "id": 503, + "name": "سردشت", + "slug": "خوزستان-سردشت", + "province_id": 13 + }, + { + "id": 504, + "name": "سوسنگرد", + "slug": "سوسنگرد", + "province_id": 13 + }, + { + "id": 505, + "name": "شادگان", + "slug": "شادگان", + "province_id": 13 + }, + { + "id": 506, + "name": "شاوور", + "slug": "شاوور", + "province_id": 13 + }, + { + "id": 507, + "name": "شرافت", + "slug": "شرافت", + "province_id": 13 + }, + { + "id": 508, + "name": "شوش", + "slug": "شوش", + "province_id": 13 + }, + { + "id": 509, + "name": "شوشتر", + "slug": "شوشتر", + "province_id": 13 + }, + { + "id": 510, + "name": "شیبان", + "slug": "شیبان", + "province_id": 13 + }, + { + "id": 511, + "name": "صالح شهر", + "slug": "صالح-شهر", + "province_id": 13 + }, + { + "id": 512, + "name": "صفی آباد", + "slug": "خوزستان-صفی-آباد", + "province_id": 13 + }, + { + "id": 513, + "name": "صیدون", + "slug": "صیدون", + "province_id": 13 + }, + { + "id": 514, + "name": "قلعه تل", + "slug": "قلعه-تل", + "province_id": 13 + }, + { + "id": 515, + "name": "قلعه خواجه", + "slug": "قلعه-خواجه", + "province_id": 13 + }, + { + "id": 516, + "name": "گتوند", + "slug": "گتوند", + "province_id": 13 + }, + { + "id": 517, + "name": "لالی", + "slug": "لالی", + "province_id": 13 + }, + { + "id": 518, + "name": "مسجدسلیمان", + "slug": "مسجدسلیمان", + "province_id": 13 + }, + { + "id": 520, + "name": "ملاثانی", + "slug": "ملاثانی", + "province_id": 13 + }, + { + "id": 521, + "name": "میانرود", + "slug": "میانرود", + "province_id": 13 + }, + { + "id": 522, + "name": "مینوشهر", + "slug": "مینوشهر", + "province_id": 13 + }, + { + "id": 523, + "name": "هفتگل", + "slug": "هفتگل", + "province_id": 13 + }, + { + "id": 524, + "name": "هندیجان", + "slug": "هندیجان", + "province_id": 13 + }, + { + "id": 525, + "name": "هویزه", + "slug": "هویزه", + "province_id": 13 + }, + { + "id": 526, + "name": "ویس", + "slug": "ویس", + "province_id": 13 + }, + { + "id": 527, + "name": "ابهر", + "slug": "ابهر", + "province_id": 14 + }, + { + "id": 528, + "name": "ارمغان خانه", + "slug": "ارمغان-خانه", + "province_id": 14 + }, + { + "id": 529, + "name": "آب بر", + "slug": "آب-بر", + "province_id": 14 + }, + { + "id": 530, + "name": "چورزق", + "slug": "چورزق", + "province_id": 14 + }, + { + "id": 531, + "name": "حلب", + "slug": "حلب", + "province_id": 14 + }, + { + "id": 532, + "name": "خرمدره", + "slug": "خرمدره", + "province_id": 14 + }, + { + "id": 533, + "name": "دندی", + "slug": "دندی", + "province_id": 14 + }, + { + "id": 534, + "name": "زرین آباد", + "slug": "زرین-آباد", + "province_id": 14 + }, + { + "id": 535, + "name": "زرین رود", + "slug": "زرین-رود", + "province_id": 14 + }, + { + "id": 536, + "name": "زنجان", + "slug": "زنجان", + "province_id": 14 + }, + { + "id": 537, + "name": "سجاس", + "slug": "سجاس", + "province_id": 14 + }, + { + "id": 538, + "name": "سلطانیه", + "slug": "سلطانیه", + "province_id": 14 + }, + { + "id": 539, + "name": "سهرورد", + "slug": "سهرورد", + "province_id": 14 + }, + { + "id": 540, + "name": "صائین قلعه", + "slug": "صائین-قلعه", + "province_id": 14 + }, + { + "id": 541, + "name": "قیدار", + "slug": "قیدار", + "province_id": 14 + }, + { + "id": 542, + "name": "گرماب", + "slug": "گرماب", + "province_id": 14 + }, + { + "id": 543, + "name": "ماه نشان", + "slug": "ماه-نشان", + "province_id": 14 + }, + { + "id": 544, + "name": "هیدج", + "slug": "هیدج", + "province_id": 14 + }, + { + "id": 545, + "name": "امیریه", + "slug": "امیریه", + "province_id": 15 + }, + { + "id": 546, + "name": "ایوانکی", + "slug": "ایوانکی", + "province_id": 15 + }, + { + "id": 547, + "name": "آرادان", + "slug": "آرادان", + "province_id": 15 + }, + { + "id": 548, + "name": "بسطام", + "slug": "بسطام", + "province_id": 15 + }, + { + "id": 549, + "name": "بیارجمند", + "slug": "بیارجمند", + "province_id": 15 + }, + { + "id": 550, + "name": "دامغان", + "slug": "دامغان", + "province_id": 15 + }, + { + "id": 551, + "name": "درجزین", + "slug": "درجزین", + "province_id": 15 + }, + { + "id": 552, + "name": "دیباج", + "slug": "دیباج", + "province_id": 15 + }, + { + "id": 553, + "name": "سرخه", + "slug": "سرخه", + "province_id": 15 + }, + { + "id": 554, + "name": "سمنان", + "slug": "سمنان", + "province_id": 15 + }, + { + "id": 555, + "name": "شاهرود", + "slug": "شاهرود", + "province_id": 15 + }, + { + "id": 556, + "name": "شهمیرزاد", + "slug": "شهمیرزاد", + "province_id": 15 + }, + { + "id": 557, + "name": "کلاته خیج", + "slug": "کلاته-خیج", + "province_id": 15 + }, + { + "id": 558, + "name": "گرمسار", + "slug": "گرمسار", + "province_id": 15 + }, + { + "id": 559, + "name": "مجن", + "slug": "مجن", + "province_id": 15 + }, + { + "id": 560, + "name": "مهدی شهر", + "slug": "مهدی-شهر", + "province_id": 15 + }, + { + "id": 561, + "name": "میامی", + "slug": "میامی", + "province_id": 15 + }, + { + "id": 562, + "name": "ادیمی", + "slug": "ادیمی", + "province_id": 16 + }, + { + "id": 563, + "name": "اسپکه", + "slug": "اسپکه", + "province_id": 16 + }, + { + "id": 564, + "name": "ایرانشهر", + "slug": "ایرانشهر", + "province_id": 16 + }, + { + "id": 565, + "name": "بزمان", + "slug": "بزمان", + "province_id": 16 + }, + { + "id": 566, + "name": "بمپور", + "slug": "بمپور", + "province_id": 16 + }, + { + "id": 567, + "name": "بنت", + "slug": "بنت", + "province_id": 16 + }, + { + "id": 568, + "name": "بنجار", + "slug": "بنجار", + "province_id": 16 + }, + { + "id": 569, + "name": "پیشین", + "slug": "پیشین", + "province_id": 16 + }, + { + "id": 570, + "name": "جالق", + "slug": "جالق", + "province_id": 16 + }, + { + "id": 571, + "name": "چابهار", + "slug": "چابهار", + "province_id": 16 + }, + { + "id": 572, + "name": "خاش", + "slug": "خاش", + "province_id": 16 + }, + { + "id": 573, + "name": "دوست محمد", + "slug": "دوست-محمد", + "province_id": 16 + }, + { + "id": 574, + "name": "راسک", + "slug": "راسک", + "province_id": 16 + }, + { + "id": 575, + "name": "زابل", + "slug": "زابل", + "province_id": 16 + }, + { + "id": 576, + "name": "زابلی", + "slug": "زابلی", + "province_id": 16 + }, + { + "id": 577, + "name": "زاهدان", + "slug": "زاهدان", + "province_id": 16 + }, + { + "id": 578, + "name": "زهک", + "slug": "زهک", + "province_id": 16 + }, + { + "id": 579, + "name": "سراوان", + "slug": "سراوان", + "province_id": 16 + }, + { + "id": 580, + "name": "سرباز", + "slug": "سرباز", + "province_id": 16 + }, + { + "id": 581, + "name": "سوران", + "slug": "سوران", + "province_id": 16 + }, + { + "id": 582, + "name": "سیرکان", + "slug": "سیرکان", + "province_id": 16 + }, + { + "id": 583, + "name": "علی اکبر", + "slug": "علی-اکبر", + "province_id": 16 + }, + { + "id": 584, + "name": "فنوج", + "slug": "فنوج", + "province_id": 16 + }, + { + "id": 585, + "name": "قصرقند", + "slug": "قصرقند", + "province_id": 16 + }, + { + "id": 586, + "name": "کنارک", + "slug": "کنارک", + "province_id": 16 + }, + { + "id": 587, + "name": "گشت", + "slug": "گشت", + "province_id": 16 + }, + { + "id": 588, + "name": "گلمورتی", + "slug": "گلمورتی", + "province_id": 16 + }, + { + "id": 589, + "name": "محمدان", + "slug": "محمدان", + "province_id": 16 + }, + { + "id": 590, + "name": "محمدآباد", + "slug": "سیستان-و-بلوچستان-محمدآباد", + "province_id": 16 + }, + { + "id": 591, + "name": "محمدی", + "slug": "محمدی", + "province_id": 16 + }, + { + "id": 592, + "name": "میرجاوه", + "slug": "میرجاوه", + "province_id": 16 + }, + { + "id": 593, + "name": "نصرت آباد", + "slug": "نصرت-آباد", + "province_id": 16 + }, + { + "id": 594, + "name": "نگور", + "slug": "نگور", + "province_id": 16 + }, + { + "id": 595, + "name": "نوک آباد", + "slug": "نوک-آباد", + "province_id": 16 + }, + { + "id": 596, + "name": "نیک شهر", + "slug": "نیک-شهر", + "province_id": 16 + }, + { + "id": 597, + "name": "هیدوچ", + "slug": "هیدوچ", + "province_id": 16 + }, + { + "id": 598, + "name": "اردکان", + "slug": "فارس-اردکان", + "province_id": 17 + }, + { + "id": 599, + "name": "ارسنجان", + "slug": "ارسنجان", + "province_id": 17 + }, + { + "id": 600, + "name": "استهبان", + "slug": "استهبان", + "province_id": 17 + }, + { + "id": 601, + "name": "اشکنان", + "slug": "اشکنان", + "province_id": 17 + }, + { + "id": 602, + "name": "افزر", + "slug": "افزر", + "province_id": 17 + }, + { + "id": 603, + "name": "اقلید", + "slug": "اقلید", + "province_id": 17 + }, + { + "id": 604, + "name": "امام شهر", + "slug": "امام-شهر", + "province_id": 17 + }, + { + "id": 605, + "name": "اهل", + "slug": "اهل", + "province_id": 17 + }, + { + "id": 606, + "name": "اوز", + "slug": "اوز", + "province_id": 17 + }, + { + "id": 607, + "name": "ایج", + "slug": "ایج", + "province_id": 17 + }, + { + "id": 608, + "name": "ایزدخواست", + "slug": "ایزدخواست", + "province_id": 17 + }, + { + "id": 609, + "name": "آباده", + "slug": "آباده", + "province_id": 17 + }, + { + "id": 610, + "name": "آباده طشک", + "slug": "آباده-طشک", + "province_id": 17 + }, + { + "id": 611, + "name": "باب انار", + "slug": "باب-انار", + "province_id": 17 + }, + { + "id": 612, + "name": "بالاده", + "slug": "فارس-بالاده", + "province_id": 17 + }, + { + "id": 613, + "name": "بنارویه", + "slug": "بنارویه", + "province_id": 17 + }, + { + "id": 614, + "name": "بهمن", + "slug": "بهمن", + "province_id": 17 + }, + { + "id": 615, + "name": "بوانات", + "slug": "بوانات", + "province_id": 17 + }, + { + "id": 616, + "name": "بیرم", + "slug": "بیرم", + "province_id": 17 + }, + { + "id": 617, + "name": "بیضا", + "slug": "بیضا", + "province_id": 17 + }, + { + "id": 618, + "name": "جنت شهر", + "slug": "جنت-شهر", + "province_id": 17 + }, + { + "id": 619, + "name": "جهرم", + "slug": "جهرم", + "province_id": 17 + }, + { + "id": 620, + "name": "جویم", + "slug": "جویم", + "province_id": 17 + }, + { + "id": 621, + "name": "زرین دشت", + "slug": "زرین-دشت", + "province_id": 17 + }, + { + "id": 622, + "name": "حسن آباد", + "slug": "فارس-حسن-آباد", + "province_id": 17 + }, + { + "id": 623, + "name": "خان زنیان", + "slug": "خان-زنیان", + "province_id": 17 + }, + { + "id": 624, + "name": "خاوران", + "slug": "خاوران", + "province_id": 17 + }, + { + "id": 625, + "name": "خرامه", + "slug": "خرامه", + "province_id": 17 + }, + { + "id": 626, + "name": "خشت", + "slug": "خشت", + "province_id": 17 + }, + { + "id": 627, + "name": "خنج", + "slug": "خنج", + "province_id": 17 + }, + { + "id": 628, + "name": "خور", + "slug": "فارس-خور", + "province_id": 17 + }, + { + "id": 629, + "name": "داراب", + "slug": "داراب", + "province_id": 17 + }, + { + "id": 630, + "name": "داریان", + "slug": "داریان", + "province_id": 17 + }, + { + "id": 631, + "name": "دبیران", + "slug": "دبیران", + "province_id": 17 + }, + { + "id": 632, + "name": "دژکرد", + "slug": "دژکرد", + "province_id": 17 + }, + { + "id": 633, + "name": "دهرم", + "slug": "دهرم", + "province_id": 17 + }, + { + "id": 634, + "name": "دوبرجی", + "slug": "دوبرجی", + "province_id": 17 + }, + { + "id": 635, + "name": "رامجرد", + "slug": "رامجرد", + "province_id": 17 + }, + { + "id": 636, + "name": "رونیز", + "slug": "رونیز", + "province_id": 17 + }, + { + "id": 637, + "name": "زاهدشهر", + "slug": "زاهدشهر", + "province_id": 17 + }, + { + "id": 638, + "name": "زرقان", + "slug": "زرقان", + "province_id": 17 + }, + { + "id": 639, + "name": "سده", + "slug": "سده", + "province_id": 17 + }, + { + "id": 640, + "name": "سروستان", + "slug": "سروستان", + "province_id": 17 + }, + { + "id": 641, + "name": "سعادت شهر", + "slug": "سعادت-شهر", + "province_id": 17 + }, + { + "id": 642, + "name": "سورمق", + "slug": "سورمق", + "province_id": 17 + }, + { + "id": 643, + "name": "سیدان", + "slug": "سیدان", + "province_id": 17 + }, + { + "id": 644, + "name": "ششده", + "slug": "ششده", + "province_id": 17 + }, + { + "id": 645, + "name": "شهرپیر", + "slug": "شهرپیر", + "province_id": 17 + }, + { + "id": 646, + "name": "شهرصدرا", + "slug": "شهرصدرا", + "province_id": 17 + }, + { + "id": 647, + "name": "شیراز", + "slug": "شیراز", + "province_id": 17 + }, + { + "id": 648, + "name": "صغاد", + "slug": "صغاد", + "province_id": 17 + }, + { + "id": 649, + "name": "صفاشهر", + "slug": "صفاشهر", + "province_id": 17 + }, + { + "id": 650, + "name": "علامرودشت", + "slug": "علامرودشت", + "province_id": 17 + }, + { + "id": 651, + "name": "فدامی", + "slug": "فدامی", + "province_id": 17 + }, + { + "id": 652, + "name": "فراشبند", + "slug": "فراشبند", + "province_id": 17 + }, + { + "id": 653, + "name": "فسا", + "slug": "فسا", + "province_id": 17 + }, + { + "id": 654, + "name": "فیروزآباد", + "slug": "فارس-فیروزآباد", + "province_id": 17 + }, + { + "id": 655, + "name": "قائمیه", + "slug": "قائمیه", + "province_id": 17 + }, + { + "id": 656, + "name": "قادرآباد", + "slug": "قادرآباد", + "province_id": 17 + }, + { + "id": 657, + "name": "قطب آباد", + "slug": "قطب-آباد", + "province_id": 17 + }, + { + "id": 658, + "name": "قطرویه", + "slug": "قطرویه", + "province_id": 17 + }, + { + "id": 659, + "name": "قیر", + "slug": "قیر", + "province_id": 17 + }, + { + "id": 660, + "name": "کارزین (فتح آباد)", + "slug": "کارزین-فتح-آباد", + "province_id": 17 + }, + { + "id": 661, + "name": "کازرون", + "slug": "کازرون", + "province_id": 17 + }, + { + "id": 662, + "name": "کامفیروز", + "slug": "کامفیروز", + "province_id": 17 + }, + { + "id": 663, + "name": "کره ای", + "slug": "کره-ای", + "province_id": 17 + }, + { + "id": 664, + "name": "کنارتخته", + "slug": "کنارتخته", + "province_id": 17 + }, + { + "id": 665, + "name": "کوار", + "slug": "کوار", + "province_id": 17 + }, + { + "id": 666, + "name": "گراش", + "slug": "گراش", + "province_id": 17 + }, + { + "id": 667, + "name": "گله دار", + "slug": "گله-دار", + "province_id": 17 + }, + { + "id": 668, + "name": "لار", + "slug": "لار", + "province_id": 17 + }, + { + "id": 669, + "name": "لامرد", + "slug": "لامرد", + "province_id": 17 + }, + { + "id": 670, + "name": "لپویی", + "slug": "لپویی", + "province_id": 17 + }, + { + "id": 671, + "name": "لطیفی", + "slug": "لطیفی", + "province_id": 17 + }, + { + "id": 672, + "name": "مبارک آباددیز", + "slug": "مبارک-آباددیز", + "province_id": 17 + }, + { + "id": 673, + "name": "مرودشت", + "slug": "مرودشت", + "province_id": 17 + }, + { + "id": 674, + "name": "مشکان", + "slug": "مشکان", + "province_id": 17 + }, + { + "id": 675, + "name": "مصیری", + "slug": "مصیری", + "province_id": 17 + }, + { + "id": 676, + "name": "مهر", + "slug": "مهر", + "province_id": 17 + }, + { + "id": 677, + "name": "میمند", + "slug": "میمند", + "province_id": 17 + }, + { + "id": 678, + "name": "نوبندگان", + "slug": "نوبندگان", + "province_id": 17 + }, + { + "id": 679, + "name": "نوجین", + "slug": "نوجین", + "province_id": 17 + }, + { + "id": 680, + "name": "نودان", + "slug": "نودان", + "province_id": 17 + }, + { + "id": 681, + "name": "نورآباد", + "slug": "فارس-نورآباد", + "province_id": 17 + }, + { + "id": 682, + "name": "نی ریز", + "slug": "نی-ریز", + "province_id": 17 + }, + { + "id": 683, + "name": "وراوی", + "slug": "وراوی", + "province_id": 17 + }, + { + "id": 684, + "name": "ارداق", + "slug": "ارداق", + "province_id": 18 + }, + { + "id": 685, + "name": "اسفرورین", + "slug": "اسفرورین", + "province_id": 18 + }, + { + "id": 686, + "name": "اقبالیه", + "slug": "اقبالیه", + "province_id": 18 + }, + { + "id": 687, + "name": "الوند", + "slug": "الوند", + "province_id": 18 + }, + { + "id": 688, + "name": "آبگرم", + "slug": "آبگرم", + "province_id": 18 + }, + { + "id": 689, + "name": "آبیک", + "slug": "آبیک", + "province_id": 18 + }, + { + "id": 690, + "name": "آوج", + "slug": "آوج", + "province_id": 18 + }, + { + "id": 691, + "name": "بوئین زهرا", + "slug": "بوئین-زهرا", + "province_id": 18 + }, + { + "id": 692, + "name": "بیدستان", + "slug": "بیدستان", + "province_id": 18 + }, + { + "id": 693, + "name": "تاکستان", + "slug": "تاکستان", + "province_id": 18 + }, + { + "id": 694, + "name": "خاکعلی", + "slug": "خاکعلی", + "province_id": 18 + }, + { + "id": 695, + "name": "خرمدشت", + "slug": "خرمدشت", + "province_id": 18 + }, + { + "id": 696, + "name": "دانسفهان", + "slug": "دانسفهان", + "province_id": 18 + }, + { + "id": 697, + "name": "رازمیان", + "slug": "رازمیان", + "province_id": 18 + }, + { + "id": 698, + "name": "سگزآباد", + "slug": "سگزآباد", + "province_id": 18 + }, + { + "id": 699, + "name": "سیردان", + "slug": "سیردان", + "province_id": 18 + }, + { + "id": 700, + "name": "شال", + "slug": "شال", + "province_id": 18 + }, + { + "id": 701, + "name": "شریفیه", + "slug": "شریفیه", + "province_id": 18 + }, + { + "id": 702, + "name": "ضیاآباد", + "slug": "ضیاآباد", + "province_id": 18 + }, + { + "id": 703, + "name": "قزوین", + "slug": "قزوین", + "province_id": 18 + }, + { + "id": 704, + "name": "کوهین", + "slug": "کوهین", + "province_id": 18 + }, + { + "id": 705, + "name": "محمدیه", + "slug": "محمدیه", + "province_id": 18 + }, + { + "id": 706, + "name": "محمودآباد نمونه", + "slug": "محمودآباد-نمونه", + "province_id": 18 + }, + { + "id": 707, + "name": "معلم کلایه", + "slug": "معلم-کلایه", + "province_id": 18 + }, + { + "id": 708, + "name": "نرجه", + "slug": "نرجه", + "province_id": 18 + }, + { + "id": 709, + "name": "جعفریه", + "slug": "جعفریه", + "province_id": 19 + }, + { + "id": 710, + "name": "دستجرد", + "slug": "دستجرد", + "province_id": 19 + }, + { + "id": 711, + "name": "سلفچگان", + "slug": "سلفچگان", + "province_id": 19 + }, + { + "id": 712, + "name": "قم", + "slug": "قم", + "province_id": 19 + }, + { + "id": 713, + "name": "قنوات", + "slug": "قنوات", + "province_id": 19 + }, + { + "id": 714, + "name": "کهک", + "slug": "کهک", + "province_id": 19 + }, + { + "id": 715, + "name": "آرمرده", + "slug": "آرمرده", + "province_id": 20 + }, + { + "id": 716, + "name": "بابارشانی", + "slug": "بابارشانی", + "province_id": 20 + }, + { + "id": 717, + "name": "بانه", + "slug": "بانه", + "province_id": 20 + }, + { + "id": 718, + "name": "بلبان آباد", + "slug": "بلبان-آباد", + "province_id": 20 + }, + { + "id": 719, + "name": "بوئین سفلی", + "slug": "بوئین-سفلی", + "province_id": 20 + }, + { + "id": 720, + "name": "بیجار", + "slug": "بیجار", + "province_id": 20 + }, + { + "id": 721, + "name": "چناره", + "slug": "چناره", + "province_id": 20 + }, + { + "id": 722, + "name": "دزج", + "slug": "دزج", + "province_id": 20 + }, + { + "id": 723, + "name": "دلبران", + "slug": "دلبران", + "province_id": 20 + }, + { + "id": 724, + "name": "دهگلان", + "slug": "دهگلان", + "province_id": 20 + }, + { + "id": 725, + "name": "دیواندره", + "slug": "دیواندره", + "province_id": 20 + }, + { + "id": 726, + "name": "زرینه", + "slug": "زرینه", + "province_id": 20 + }, + { + "id": 727, + "name": "سروآباد", + "slug": "سروآباد", + "province_id": 20 + }, + { + "id": 728, + "name": "سریش آباد", + "slug": "سریش-آباد", + "province_id": 20 + }, + { + "id": 729, + "name": "سقز", + "slug": "سقز", + "province_id": 20 + }, + { + "id": 730, + "name": "سنندج", + "slug": "سنندج", + "province_id": 20 + }, + { + "id": 731, + "name": "شویشه", + "slug": "شویشه", + "province_id": 20 + }, + { + "id": 732, + "name": "صاحب", + "slug": "صاحب", + "province_id": 20 + }, + { + "id": 733, + "name": "قروه", + "slug": "قروه", + "province_id": 20 + }, + { + "id": 734, + "name": "کامیاران", + "slug": "کامیاران", + "province_id": 20 + }, + { + "id": 735, + "name": "کانی دینار", + "slug": "کانی-دینار", + "province_id": 20 + }, + { + "id": 736, + "name": "کانی سور", + "slug": "کانی-سور", + "province_id": 20 + }, + { + "id": 737, + "name": "مریوان", + "slug": "مریوان", + "province_id": 20 + }, + { + "id": 738, + "name": "موچش", + "slug": "موچش", + "province_id": 20 + }, + { + "id": 739, + "name": "یاسوکند", + "slug": "یاسوکند", + "province_id": 20 + }, + { + "id": 740, + "name": "اختیارآباد", + "slug": "اختیارآباد", + "province_id": 21 + }, + { + "id": 741, + "name": "ارزوئیه", + "slug": "ارزوئیه", + "province_id": 21 + }, + { + "id": 742, + "name": "امین شهر", + "slug": "امین-شهر", + "province_id": 21 + }, + { + "id": 743, + "name": "انار", + "slug": "انار", + "province_id": 21 + }, + { + "id": 744, + "name": "اندوهجرد", + "slug": "اندوهجرد", + "province_id": 21 + }, + { + "id": 745, + "name": "باغین", + "slug": "باغین", + "province_id": 21 + }, + { + "id": 746, + "name": "بافت", + "slug": "بافت", + "province_id": 21 + }, + { + "id": 747, + "name": "بردسیر", + "slug": "بردسیر", + "province_id": 21 + }, + { + "id": 748, + "name": "بروات", + "slug": "بروات", + "province_id": 21 + }, + { + "id": 749, + "name": "بزنجان", + "slug": "بزنجان", + "province_id": 21 + }, + { + "id": 750, + "name": "بم", + "slug": "بم", + "province_id": 21 + }, + { + "id": 751, + "name": "بهرمان", + "slug": "بهرمان", + "province_id": 21 + }, + { + "id": 752, + "name": "پاریز", + "slug": "پاریز", + "province_id": 21 + }, + { + "id": 753, + "name": "جبالبارز", + "slug": "جبالبارز", + "province_id": 21 + }, + { + "id": 754, + "name": "جوپار", + "slug": "جوپار", + "province_id": 21 + }, + { + "id": 755, + "name": "جوزم", + "slug": "جوزم", + "province_id": 21 + }, + { + "id": 756, + "name": "جیرفت", + "slug": "جیرفت", + "province_id": 21 + }, + { + "id": 757, + "name": "چترود", + "slug": "چترود", + "province_id": 21 + }, + { + "id": 758, + "name": "خاتون آباد", + "slug": "خاتون-آباد", + "province_id": 21 + }, + { + "id": 759, + "name": "خانوک", + "slug": "خانوک", + "province_id": 21 + }, + { + "id": 760, + "name": "خورسند", + "slug": "خورسند", + "province_id": 21 + }, + { + "id": 761, + "name": "درب بهشت", + "slug": "درب-بهشت", + "province_id": 21 + }, + { + "id": 762, + "name": "دهج", + "slug": "دهج", + "province_id": 21 + }, + { + "id": 763, + "name": "رابر", + "slug": "رابر", + "province_id": 21 + }, + { + "id": 764, + "name": "راور", + "slug": "راور", + "province_id": 21 + }, + { + "id": 765, + "name": "راین", + "slug": "راین", + "province_id": 21 + }, + { + "id": 766, + "name": "رفسنجان", + "slug": "رفسنجان", + "province_id": 21 + }, + { + "id": 767, + "name": "رودبار", + "slug": "کرمان-رودبار", + "province_id": 21 + }, + { + "id": 768, + "name": "ریحان شهر", + "slug": "ریحان-شهر", + "province_id": 21 + }, + { + "id": 769, + "name": "زرند", + "slug": "زرند", + "province_id": 21 + }, + { + "id": 770, + "name": "زنگی آباد", + "slug": "زنگی-آباد", + "province_id": 21 + }, + { + "id": 771, + "name": "زیدآباد", + "slug": "زیدآباد", + "province_id": 21 + }, + { + "id": 772, + "name": "سیرجان", + "slug": "سیرجان", + "province_id": 21 + }, + { + "id": 773, + "name": "شهداد", + "slug": "شهداد", + "province_id": 21 + }, + { + "id": 774, + "name": "شهربابک", + "slug": "شهربابک", + "province_id": 21 + }, + { + "id": 775, + "name": "صفائیه", + "slug": "صفائیه", + "province_id": 21 + }, + { + "id": 776, + "name": "عنبرآباد", + "slug": "عنبرآباد", + "province_id": 21 + }, + { + "id": 777, + "name": "فاریاب", + "slug": "فاریاب", + "province_id": 21 + }, + { + "id": 778, + "name": "فهرج", + "slug": "فهرج", + "province_id": 21 + }, + { + "id": 779, + "name": "قلعه گنج", + "slug": "قلعه-گنج", + "province_id": 21 + }, + { + "id": 780, + "name": "کاظم آباد", + "slug": "کاظم-آباد", + "province_id": 21 + }, + { + "id": 781, + "name": "کرمان", + "slug": "کرمان", + "province_id": 21 + }, + { + "id": 782, + "name": "کشکوئیه", + "slug": "کشکوئیه", + "province_id": 21 + }, + { + "id": 783, + "name": "کهنوج", + "slug": "کهنوج", + "province_id": 21 + }, + { + "id": 784, + "name": "کوهبنان", + "slug": "کوهبنان", + "province_id": 21 + }, + { + "id": 785, + "name": "کیانشهر", + "slug": "کیانشهر", + "province_id": 21 + }, + { + "id": 786, + "name": "گلباف", + "slug": "گلباف", + "province_id": 21 + }, + { + "id": 787, + "name": "گلزار", + "slug": "گلزار", + "province_id": 21 + }, + { + "id": 788, + "name": "لاله زار", + "slug": "لاله-زار", + "province_id": 21 + }, + { + "id": 789, + "name": "ماهان", + "slug": "ماهان", + "province_id": 21 + }, + { + "id": 790, + "name": "محمدآباد", + "slug": "کرمان-محمدآباد", + "province_id": 21 + }, + { + "id": 791, + "name": "محی آباد", + "slug": "محی-آباد", + "province_id": 21 + }, + { + "id": 792, + "name": "مردهک", + "slug": "مردهک", + "province_id": 21 + }, + { + "id": 793, + "name": "مس سرچشمه", + "slug": "مس-سرچشمه", + "province_id": 21 + }, + { + "id": 794, + "name": "منوجان", + "slug": "منوجان", + "province_id": 21 + }, + { + "id": 795, + "name": "نجف شهر", + "slug": "نجف-شهر", + "province_id": 21 + }, + { + "id": 796, + "name": "نرماشیر", + "slug": "نرماشیر", + "province_id": 21 + }, + { + "id": 797, + "name": "نظام شهر", + "slug": "نظام-شهر", + "province_id": 21 + }, + { + "id": 798, + "name": "نگار", + "slug": "نگار", + "province_id": 21 + }, + { + "id": 799, + "name": "نودژ", + "slug": "نودژ", + "province_id": 21 + }, + { + "id": 800, + "name": "هجدک", + "slug": "هجدک", + "province_id": 21 + }, + { + "id": 801, + "name": "یزدان شهر", + "slug": "یزدان-شهر", + "province_id": 21 + }, + { + "id": 802, + "name": "ازگله", + "slug": "ازگله", + "province_id": 22 + }, + { + "id": 803, + "name": "اسلام آباد غرب", + "slug": "اسلام-آباد-غرب", + "province_id": 22 + }, + { + "id": 804, + "name": "باینگان", + "slug": "باینگان", + "province_id": 22 + }, + { + "id": 805, + "name": "بیستون", + "slug": "بیستون", + "province_id": 22 + }, + { + "id": 806, + "name": "پاوه", + "slug": "پاوه", + "province_id": 22 + }, + { + "id": 807, + "name": "تازه آباد", + "slug": "تازه-آباد", + "province_id": 22 + }, + { + "id": 808, + "name": "جوان رود", + "slug": "جوان-رود", + "province_id": 22 + }, + { + "id": 809, + "name": "حمیل", + "slug": "حمیل", + "province_id": 22 + }, + { + "id": 810, + "name": "ماهیدشت", + "slug": "ماهیدشت", + "province_id": 22 + }, + { + "id": 811, + "name": "روانسر", + "slug": "روانسر", + "province_id": 22 + }, + { + "id": 812, + "name": "سرپل ذهاب", + "slug": "سرپل-ذهاب", + "province_id": 22 + }, + { + "id": 813, + "name": "سرمست", + "slug": "سرمست", + "province_id": 22 + }, + { + "id": 814, + "name": "سطر", + "slug": "سطر", + "province_id": 22 + }, + { + "id": 815, + "name": "سنقر", + "slug": "سنقر", + "province_id": 22 + }, + { + "id": 816, + "name": "سومار", + "slug": "سومار", + "province_id": 22 + }, + { + "id": 817, + "name": "شاهو", + "slug": "شاهو", + "province_id": 22 + }, + { + "id": 818, + "name": "صحنه", + "slug": "صحنه", + "province_id": 22 + }, + { + "id": 819, + "name": "قصرشیرین", + "slug": "قصرشیرین", + "province_id": 22 + }, + { + "id": 820, + "name": "کرمانشاه", + "slug": "کرمانشاه", + "province_id": 22 + }, + { + "id": 821, + "name": "کرندغرب", + "slug": "کرندغرب", + "province_id": 22 + }, + { + "id": 822, + "name": "کنگاور", + "slug": "کنگاور", + "province_id": 22 + }, + { + "id": 823, + "name": "کوزران", + "slug": "کوزران", + "province_id": 22 + }, + { + "id": 824, + "name": "گهواره", + "slug": "گهواره", + "province_id": 22 + }, + { + "id": 825, + "name": "گیلانغرب", + "slug": "گیلانغرب", + "province_id": 22 + }, + { + "id": 826, + "name": "میان راهان", + "slug": "میان-راهان", + "province_id": 22 + }, + { + "id": 827, + "name": "نودشه", + "slug": "نودشه", + "province_id": 22 + }, + { + "id": 828, + "name": "نوسود", + "slug": "نوسود", + "province_id": 22 + }, + { + "id": 829, + "name": "هرسین", + "slug": "هرسین", + "province_id": 22 + }, + { + "id": 830, + "name": "هلشی", + "slug": "هلشی", + "province_id": 22 + }, + { + "id": 831, + "name": "باشت", + "slug": "باشت", + "province_id": 23 + }, + { + "id": 832, + "name": "پاتاوه", + "slug": "پاتاوه", + "province_id": 23 + }, + { + "id": 833, + "name": "چرام", + "slug": "چرام", + "province_id": 23 + }, + { + "id": 834, + "name": "چیتاب", + "slug": "چیتاب", + "province_id": 23 + }, + { + "id": 835, + "name": "دهدشت", + "slug": "دهدشت", + "province_id": 23 + }, + { + "id": 836, + "name": "دوگنبدان", + "slug": "دوگنبدان", + "province_id": 23 + }, + { + "id": 837, + "name": "دیشموک", + "slug": "دیشموک", + "province_id": 23 + }, + { + "id": 838, + "name": "سوق", + "slug": "سوق", + "province_id": 23 + }, + { + "id": 839, + "name": "سی سخت", + "slug": "سی-سخت", + "province_id": 23 + }, + { + "id": 840, + "name": "قلعه رئیسی", + "slug": "قلعه-رئیسی", + "province_id": 23 + }, + { + "id": 841, + "name": "گراب سفلی", + "slug": "گراب-سفلی", + "province_id": 23 + }, + { + "id": 842, + "name": "لنده", + "slug": "لنده", + "province_id": 23 + }, + { + "id": 843, + "name": "لیکک", + "slug": "لیکک", + "province_id": 23 + }, + { + "id": 844, + "name": "مادوان", + "slug": "مادوان", + "province_id": 23 + }, + { + "id": 845, + "name": "مارگون", + "slug": "مارگون", + "province_id": 23 + }, + { + "id": 846, + "name": "یاسوج", + "slug": "یاسوج", + "province_id": 23 + }, + { + "id": 847, + "name": "انبارآلوم", + "slug": "انبارآلوم", + "province_id": 24 + }, + { + "id": 848, + "name": "اینچه برون", + "slug": "اینچه-برون", + "province_id": 24 + }, + { + "id": 849, + "name": "آزادشهر", + "slug": "آزادشهر", + "province_id": 24 + }, + { + "id": 850, + "name": "آق قلا", + "slug": "آق-قلا", + "province_id": 24 + }, + { + "id": 851, + "name": "بندرترکمن", + "slug": "بندرترکمن", + "province_id": 24 + }, + { + "id": 852, + "name": "بندرگز", + "slug": "بندرگز", + "province_id": 24 + }, + { + "id": 853, + "name": "جلین", + "slug": "جلین", + "province_id": 24 + }, + { + "id": 854, + "name": "خان ببین", + "slug": "خان-ببین", + "province_id": 24 + }, + { + "id": 855, + "name": "دلند", + "slug": "دلند", + "province_id": 24 + }, + { + "id": 856, + "name": "رامیان", + "slug": "رامیان", + "province_id": 24 + }, + { + "id": 857, + "name": "سرخنکلاته", + "slug": "سرخنکلاته", + "province_id": 24 + }, + { + "id": 858, + "name": "سیمین شهر", + "slug": "سیمین-شهر", + "province_id": 24 + }, + { + "id": 859, + "name": "علی آباد کتول", + "slug": "علی-آباد-کتول", + "province_id": 24 + }, + { + "id": 860, + "name": "فاضل آباد", + "slug": "فاضل-آباد", + "province_id": 24 + }, + { + "id": 861, + "name": "کردکوی", + "slug": "کردکوی", + "province_id": 24 + }, + { + "id": 862, + "name": "کلاله", + "slug": "کلاله", + "province_id": 24 + }, + { + "id": 863, + "name": "گالیکش", + "slug": "گالیکش", + "province_id": 24 + }, + { + "id": 864, + "name": "گرگان", + "slug": "گرگان", + "province_id": 24 + }, + { + "id": 865, + "name": "گمیش تپه", + "slug": "گمیش-تپه", + "province_id": 24 + }, + { + "id": 866, + "name": "گنبدکاووس", + "slug": "گنبدکاووس", + "province_id": 24 + }, + { + "id": 867, + "name": "مراوه", + "slug": "مراوه", + "province_id": 24 + }, + { + "id": 868, + "name": "مینودشت", + "slug": "مینودشت", + "province_id": 24 + }, + { + "id": 869, + "name": "نگین شهر", + "slug": "نگین-شهر", + "province_id": 24 + }, + { + "id": 870, + "name": "نوده خاندوز", + "slug": "نوده-خاندوز", + "province_id": 24 + }, + { + "id": 871, + "name": "نوکنده", + "slug": "نوکنده", + "province_id": 24 + }, + { + "id": 872, + "name": "ازنا", + "slug": "ازنا", + "province_id": 25 + }, + { + "id": 873, + "name": "اشترینان", + "slug": "اشترینان", + "province_id": 25 + }, + { + "id": 874, + "name": "الشتر", + "slug": "الشتر", + "province_id": 25 + }, + { + "id": 875, + "name": "الیگودرز", + "slug": "الیگودرز", + "province_id": 25 + }, + { + "id": 876, + "name": "بروجرد", + "slug": "بروجرد", + "province_id": 25 + }, + { + "id": 877, + "name": "پلدختر", + "slug": "پلدختر", + "province_id": 25 + }, + { + "id": 878, + "name": "چالانچولان", + "slug": "چالانچولان", + "province_id": 25 + }, + { + "id": 879, + "name": "چغلوندی", + "slug": "چغلوندی", + "province_id": 25 + }, + { + "id": 880, + "name": "چقابل", + "slug": "چقابل", + "province_id": 25 + }, + { + "id": 881, + "name": "خرم آباد", + "slug": "لرستان-خرم-آباد", + "province_id": 25 + }, + { + "id": 882, + "name": "درب گنبد", + "slug": "درب-گنبد", + "province_id": 25 + }, + { + "id": 883, + "name": "دورود", + "slug": "دورود", + "province_id": 25 + }, + { + "id": 884, + "name": "زاغه", + "slug": "زاغه", + "province_id": 25 + }, + { + "id": 885, + "name": "سپیددشت", + "slug": "سپیددشت", + "province_id": 25 + }, + { + "id": 886, + "name": "سراب دوره", + "slug": "سراب-دوره", + "province_id": 25 + }, + { + "id": 887, + "name": "فیروزآباد", + "slug": "لرستان-فیروزآباد", + "province_id": 25 + }, + { + "id": 888, + "name": "کونانی", + "slug": "کونانی", + "province_id": 25 + }, + { + "id": 889, + "name": "کوهدشت", + "slug": "کوهدشت", + "province_id": 25 + }, + { + "id": 890, + "name": "گراب", + "slug": "گراب", + "province_id": 25 + }, + { + "id": 891, + "name": "معمولان", + "slug": "معمولان", + "province_id": 25 + }, + { + "id": 892, + "name": "مومن آباد", + "slug": "مومن-آباد", + "province_id": 25 + }, + { + "id": 893, + "name": "نورآباد", + "slug": "لرستان-نورآباد", + "province_id": 25 + }, + { + "id": 894, + "name": "ویسیان", + "slug": "ویسیان", + "province_id": 25 + }, + { + "id": 895, + "name": "احمدسرگوراب", + "slug": "احمدسرگوراب", + "province_id": 26 + }, + { + "id": 896, + "name": "اسالم", + "slug": "اسالم", + "province_id": 26 + }, + { + "id": 897, + "name": "اطاقور", + "slug": "اطاقور", + "province_id": 26 + }, + { + "id": 898, + "name": "املش", + "slug": "املش", + "province_id": 26 + }, + { + "id": 899, + "name": "آستارا", + "slug": "آستارا", + "province_id": 26 + }, + { + "id": 900, + "name": "آستانه اشرفیه", + "slug": "آستانه-اشرفیه", + "province_id": 26 + }, + { + "id": 901, + "name": "بازار جمعه", + "slug": "بازار-جمعه", + "province_id": 26 + }, + { + "id": 902, + "name": "بره سر", + "slug": "بره-سر", + "province_id": 26 + }, + { + "id": 903, + "name": "بندرانزلی", + "slug": "بندرانزلی", + "province_id": 26 + }, + { + "id": 906, + "name": "پره سر", + "slug": "پره-سر", + "province_id": 26 + }, + { + "id": 907, + "name": "تالش", + "slug": "تالش", + "province_id": 26 + }, + { + "id": 908, + "name": "توتکابن", + "slug": "توتکابن", + "province_id": 26 + }, + { + "id": 909, + "name": "جیرنده", + "slug": "جیرنده", + "province_id": 26 + }, + { + "id": 910, + "name": "چابکسر", + "slug": "چابکسر", + "province_id": 26 + }, + { + "id": 911, + "name": "چاف و چمخاله", + "slug": "چاف-و-چمخاله", + "province_id": 26 + }, + { + "id": 912, + "name": "چوبر", + "slug": "چوبر", + "province_id": 26 + }, + { + "id": 913, + "name": "حویق", + "slug": "حویق", + "province_id": 26 + }, + { + "id": 914, + "name": "خشکبیجار", + "slug": "خشکبیجار", + "province_id": 26 + }, + { + "id": 915, + "name": "خمام", + "slug": "خمام", + "province_id": 26 + }, + { + "id": 916, + "name": "دیلمان", + "slug": "دیلمان", + "province_id": 26 + }, + { + "id": 917, + "name": "رانکوه", + "slug": "رانکوه", + "province_id": 26 + }, + { + "id": 918, + "name": "رحیم آباد", + "slug": "رحیم-آباد", + "province_id": 26 + }, + { + "id": 919, + "name": "رستم آباد", + "slug": "رستم-آباد", + "province_id": 26 + }, + { + "id": 920, + "name": "رشت", + "slug": "رشت", + "province_id": 26 + }, + { + "id": 921, + "name": "رضوانشهر", + "slug": "گیلان-رضوانشهر", + "province_id": 26 + }, + { + "id": 922, + "name": "رودبار", + "slug": "گیلان-رودبار", + "province_id": 26 + }, + { + "id": 923, + "name": "رودبنه", + "slug": "رودبنه", + "province_id": 26 + }, + { + "id": 924, + "name": "رودسر", + "slug": "رودسر", + "province_id": 26 + }, + { + "id": 925, + "name": "سنگر", + "slug": "سنگر", + "province_id": 26 + }, + { + "id": 926, + "name": "سیاهکل", + "slug": "سیاهکل", + "province_id": 26 + }, + { + "id": 927, + "name": "شفت", + "slug": "شفت", + "province_id": 26 + }, + { + "id": 928, + "name": "شلمان", + "slug": "شلمان", + "province_id": 26 + }, + { + "id": 929, + "name": "صومعه سرا", + "slug": "صومعه-سرا", + "province_id": 26 + }, + { + "id": 930, + "name": "فومن", + "slug": "فومن", + "province_id": 26 + }, + { + "id": 931, + "name": "کلاچای", + "slug": "کلاچای", + "province_id": 26 + }, + { + "id": 932, + "name": "کوچصفهان", + "slug": "کوچصفهان", + "province_id": 26 + }, + { + "id": 933, + "name": "کومله", + "slug": "کومله", + "province_id": 26 + }, + { + "id": 934, + "name": "کیاشهر", + "slug": "کیاشهر", + "province_id": 26 + }, + { + "id": 935, + "name": "گوراب زرمیخ", + "slug": "گوراب-زرمیخ", + "province_id": 26 + }, + { + "id": 936, + "name": "لاهیجان", + "slug": "لاهیجان", + "province_id": 26 + }, + { + "id": 937, + "name": "لشت نشا", + "slug": "لشت-نشا", + "province_id": 26 + }, + { + "id": 938, + "name": "لنگرود", + "slug": "لنگرود", + "province_id": 26 + }, + { + "id": 939, + "name": "لوشان", + "slug": "لوشان", + "province_id": 26 + }, + { + "id": 940, + "name": "لولمان", + "slug": "لولمان", + "province_id": 26 + }, + { + "id": 941, + "name": "لوندویل", + "slug": "لوندویل", + "province_id": 26 + }, + { + "id": 942, + "name": "لیسار", + "slug": "لیسار", + "province_id": 26 + }, + { + "id": 943, + "name": "ماسال", + "slug": "ماسال", + "province_id": 26 + }, + { + "id": 944, + "name": "ماسوله", + "slug": "ماسوله", + "province_id": 26 + }, + { + "id": 945, + "name": "مرجقل", + "slug": "مرجقل", + "province_id": 26 + }, + { + "id": 946, + "name": "منجیل", + "slug": "منجیل", + "province_id": 26 + }, + { + "id": 947, + "name": "واجارگاه", + "slug": "واجارگاه", + "province_id": 26 + }, + { + "id": 948, + "name": "امیرکلا", + "slug": "امیرکلا", + "province_id": 27 + }, + { + "id": 949, + "name": "ایزدشهر", + "slug": "ایزدشهر", + "province_id": 27 + }, + { + "id": 950, + "name": "آلاشت", + "slug": "آلاشت", + "province_id": 27 + }, + { + "id": 951, + "name": "آمل", + "slug": "آمل", + "province_id": 27 + }, + { + "id": 952, + "name": "بابل", + "slug": "بابل", + "province_id": 27 + }, + { + "id": 953, + "name": "بابلسر", + "slug": "بابلسر", + "province_id": 27 + }, + { + "id": 954, + "name": "بلده", + "slug": "مازندران-بلده", + "province_id": 27 + }, + { + "id": 955, + "name": "بهشهر", + "slug": "بهشهر", + "province_id": 27 + }, + { + "id": 956, + "name": "بهنمیر", + "slug": "بهنمیر", + "province_id": 27 + }, + { + "id": 957, + "name": "پل سفید", + "slug": "پل-سفید", + "province_id": 27 + }, + { + "id": 958, + "name": "تنکابن", + "slug": "تنکابن", + "province_id": 27 + }, + { + "id": 959, + "name": "جویبار", + "slug": "جویبار", + "province_id": 27 + }, + { + "id": 960, + "name": "چالوس", + "slug": "چالوس", + "province_id": 27 + }, + { + "id": 961, + "name": "چمستان", + "slug": "چمستان", + "province_id": 27 + }, + { + "id": 962, + "name": "خرم آباد", + "slug": "مازندران-خرم-آباد", + "province_id": 27 + }, + { + "id": 963, + "name": "خلیل شهر", + "slug": "خلیل-شهر", + "province_id": 27 + }, + { + "id": 964, + "name": "خوش رودپی", + "slug": "خوش-رودپی", + "province_id": 27 + }, + { + "id": 965, + "name": "دابودشت", + "slug": "دابودشت", + "province_id": 27 + }, + { + "id": 966, + "name": "رامسر", + "slug": "رامسر", + "province_id": 27 + }, + { + "id": 967, + "name": "رستمکلا", + "slug": "رستمکلا", + "province_id": 27 + }, + { + "id": 968, + "name": "رویان", + "slug": "رویان", + "province_id": 27 + }, + { + "id": 969, + "name": "رینه", + "slug": "رینه", + "province_id": 27 + }, + { + "id": 970, + "name": "زرگرمحله", + "slug": "زرگرمحله", + "province_id": 27 + }, + { + "id": 971, + "name": "زیرآب", + "slug": "زیرآب", + "province_id": 27 + }, + { + "id": 972, + "name": "ساری", + "slug": "ساری", + "province_id": 27 + }, + { + "id": 973, + "name": "سرخرود", + "slug": "سرخرود", + "province_id": 27 + }, + { + "id": 974, + "name": "سلمان شهر", + "slug": "سلمان-شهر", + "province_id": 27 + }, + { + "id": 975, + "name": "سورک", + "slug": "سورک", + "province_id": 27 + }, + { + "id": 976, + "name": "شیرگاه", + "slug": "شیرگاه", + "province_id": 27 + }, + { + "id": 977, + "name": "شیرود", + "slug": "شیرود", + "province_id": 27 + }, + { + "id": 978, + "name": "عباس آباد", + "slug": "عباس-آباد", + "province_id": 27 + }, + { + "id": 979, + "name": "فریدونکنار", + "slug": "فریدونکنار", + "province_id": 27 + }, + { + "id": 980, + "name": "فریم", + "slug": "فریم", + "province_id": 27 + }, + { + "id": 981, + "name": "قائم شهر", + "slug": "قائم-شهر", + "province_id": 27 + }, + { + "id": 982, + "name": "کتالم", + "slug": "کتالم", + "province_id": 27 + }, + { + "id": 983, + "name": "کلارآباد", + "slug": "کلارآباد", + "province_id": 27 + }, + { + "id": 984, + "name": "کلاردشت", + "slug": "کلاردشت", + "province_id": 27 + }, + { + "id": 985, + "name": "کله بست", + "slug": "کله-بست", + "province_id": 27 + }, + { + "id": 986, + "name": "کوهی خیل", + "slug": "کوهی-خیل", + "province_id": 27 + }, + { + "id": 987, + "name": "کیاسر", + "slug": "کیاسر", + "province_id": 27 + }, + { + "id": 988, + "name": "کیاکلا", + "slug": "کیاکلا", + "province_id": 27 + }, + { + "id": 989, + "name": "گتاب", + "slug": "گتاب", + "province_id": 27 + }, + { + "id": 990, + "name": "گزنک", + "slug": "گزنک", + "province_id": 27 + }, + { + "id": 991, + "name": "گلوگاه", + "slug": "گلوگاه", + "province_id": 27 + }, + { + "id": 992, + "name": "محمودآباد", + "slug": "مازندران-محمودآباد", + "province_id": 27 + }, + { + "id": 993, + "name": "مرزن آباد", + "slug": "مرزن-آباد", + "province_id": 27 + }, + { + "id": 994, + "name": "مرزیکلا", + "slug": "مرزیکلا", + "province_id": 27 + }, + { + "id": 995, + "name": "نشتارود", + "slug": "نشتارود", + "province_id": 27 + }, + { + "id": 996, + "name": "نکا", + "slug": "نکا", + "province_id": 27 + }, + { + "id": 997, + "name": "نور", + "slug": "نور", + "province_id": 27 + }, + { + "id": 998, + "name": "نوشهر", + "slug": "نوشهر", + "province_id": 27 + }, + { + "id": 999, + "name": "اراک", + "slug": "اراک", + "province_id": 28 + }, + { + "id": 1000, + "name": "آستانه", + "slug": "آستانه", + "province_id": 28 + }, + { + "id": 1001, + "name": "آشتیان", + "slug": "آشتیان", + "province_id": 28 + }, + { + "id": 1002, + "name": "پرندک", + "slug": "پرندک", + "province_id": 28 + }, + { + "id": 1003, + "name": "تفرش", + "slug": "تفرش", + "province_id": 28 + }, + { + "id": 1004, + "name": "توره", + "slug": "توره", + "province_id": 28 + }, + { + "id": 1005, + "name": "جاورسیان", + "slug": "جاورسیان", + "province_id": 28 + }, + { + "id": 1006, + "name": "خشکرود", + "slug": "خشکرود", + "province_id": 28 + }, + { + "id": 1007, + "name": "خمین", + "slug": "خمین", + "province_id": 28 + }, + { + "id": 1008, + "name": "خنداب", + "slug": "خنداب", + "province_id": 28 + }, + { + "id": 1009, + "name": "داودآباد", + "slug": "داودآباد", + "province_id": 28 + }, + { + "id": 1010, + "name": "دلیجان", + "slug": "دلیجان", + "province_id": 28 + }, + { + "id": 1011, + "name": "رازقان", + "slug": "رازقان", + "province_id": 28 + }, + { + "id": 1012, + "name": "زاویه", + "slug": "زاویه", + "province_id": 28 + }, + { + "id": 1013, + "name": "ساروق", + "slug": "ساروق", + "province_id": 28 + }, + { + "id": 1014, + "name": "ساوه", + "slug": "ساوه", + "province_id": 28 + }, + { + "id": 1015, + "name": "سنجان", + "slug": "سنجان", + "province_id": 28 + }, + { + "id": 1016, + "name": "شازند", + "slug": "شازند", + "province_id": 28 + }, + { + "id": 1017, + "name": "غرق آباد", + "slug": "غرق-آباد", + "province_id": 28 + }, + { + "id": 1018, + "name": "فرمهین", + "slug": "فرمهین", + "province_id": 28 + }, + { + "id": 1019, + "name": "قورچی باشی", + "slug": "قورچی-باشی", + "province_id": 28 + }, + { + "id": 1020, + "name": "کرهرود", + "slug": "کرهرود", + "province_id": 28 + }, + { + "id": 1021, + "name": "کمیجان", + "slug": "کمیجان", + "province_id": 28 + }, + { + "id": 1022, + "name": "مامونیه", + "slug": "مامونیه", + "province_id": 28 + }, + { + "id": 1023, + "name": "محلات", + "slug": "محلات", + "province_id": 28 + }, + { + "id": 1024, + "name": "مهاجران", + "slug": "مهاجران", + "province_id": 28 + }, + { + "id": 1025, + "name": "میلاجرد", + "slug": "میلاجرد", + "province_id": 28 + }, + { + "id": 1026, + "name": "نراق", + "slug": "نراق", + "province_id": 28 + }, + { + "id": 1027, + "name": "نوبران", + "slug": "نوبران", + "province_id": 28 + }, + { + "id": 1028, + "name": "نیمور", + "slug": "نیمور", + "province_id": 28 + }, + { + "id": 1029, + "name": "هندودر", + "slug": "هندودر", + "province_id": 28 + }, + { + "id": 1030, + "name": "ابوموسی", + "slug": "ابوموسی", + "province_id": 29 + }, + { + "id": 1031, + "name": "بستک", + "slug": "بستک", + "province_id": 29 + }, + { + "id": 1032, + "name": "بندرجاسک", + "slug": "بندرجاسک", + "province_id": 29 + }, + { + "id": 1033, + "name": "بندرچارک", + "slug": "بندرچارک", + "province_id": 29 + }, + { + "id": 1034, + "name": "بندرخمیر", + "slug": "بندرخمیر", + "province_id": 29 + }, + { + "id": 1035, + "name": "بندرعباس", + "slug": "بندرعباس", + "province_id": 29 + }, + { + "id": 1036, + "name": "بندرلنگه", + "slug": "بندرلنگه", + "province_id": 29 + }, + { + "id": 1037, + "name": "بیکا", + "slug": "بیکا", + "province_id": 29 + }, + { + "id": 1038, + "name": "پارسیان", + "slug": "پارسیان", + "province_id": 29 + }, + { + "id": 1039, + "name": "تخت", + "slug": "تخت", + "province_id": 29 + }, + { + "id": 1040, + "name": "جناح", + "slug": "جناح", + "province_id": 29 + }, + { + "id": 1041, + "name": "حاجی آباد", + "slug": "هرمزگان-حاجی-آباد", + "province_id": 29 + }, + { + "id": 1042, + "name": "درگهان", + "slug": "درگهان", + "province_id": 29 + }, + { + "id": 1043, + "name": "دهبارز", + "slug": "دهبارز", + "province_id": 29 + }, + { + "id": 1044, + "name": "رویدر", + "slug": "رویدر", + "province_id": 29 + }, + { + "id": 1045, + "name": "زیارتعلی", + "slug": "زیارتعلی", + "province_id": 29 + }, + { + "id": 1046, + "name": "سردشت", + "slug": "هرمزگان-سردشت", + "province_id": 29 + }, + { + "id": 1047, + "name": "سندرک", + "slug": "سندرک", + "province_id": 29 + }, + { + "id": 1048, + "name": "سوزا", + "slug": "سوزا", + "province_id": 29 + }, + { + "id": 1049, + "name": "سیریک", + "slug": "سیریک", + "province_id": 29 + }, + { + "id": 1050, + "name": "فارغان", + "slug": "فارغان", + "province_id": 29 + }, + { + "id": 1051, + "name": "فین", + "slug": "فین", + "province_id": 29 + }, + { + "id": 1052, + "name": "قشم", + "slug": "قشم", + "province_id": 29 + }, + { + "id": 1053, + "name": "قلعه قاضی", + "slug": "قلعه-قاضی", + "province_id": 29 + }, + { + "id": 1054, + "name": "کنگ", + "slug": "کنگ", + "province_id": 29 + }, + { + "id": 1055, + "name": "کوشکنار", + "slug": "کوشکنار", + "province_id": 29 + }, + { + "id": 1056, + "name": "کیش", + "slug": "کیش", + "province_id": 29 + }, + { + "id": 1057, + "name": "گوهران", + "slug": "گوهران", + "province_id": 29 + }, + { + "id": 1058, + "name": "میناب", + "slug": "میناب", + "province_id": 29 + }, + { + "id": 1059, + "name": "هرمز", + "slug": "هرمز", + "province_id": 29 + }, + { + "id": 1060, + "name": "هشتبندی", + "slug": "هشتبندی", + "province_id": 29 + }, + { + "id": 1061, + "name": "ازندریان", + "slug": "ازندریان", + "province_id": 30 + }, + { + "id": 1062, + "name": "اسدآباد", + "slug": "اسدآباد", + "province_id": 30 + }, + { + "id": 1063, + "name": "برزول", + "slug": "برزول", + "province_id": 30 + }, + { + "id": 1064, + "name": "بهار", + "slug": "بهار", + "province_id": 30 + }, + { + "id": 1065, + "name": "تویسرکان", + "slug": "تویسرکان", + "province_id": 30 + }, + { + "id": 1066, + "name": "جورقان", + "slug": "جورقان", + "province_id": 30 + }, + { + "id": 1067, + "name": "جوکار", + "slug": "جوکار", + "province_id": 30 + }, + { + "id": 1068, + "name": "دمق", + "slug": "دمق", + "province_id": 30 + }, + { + "id": 1069, + "name": "رزن", + "slug": "رزن", + "province_id": 30 + }, + { + "id": 1070, + "name": "زنگنه", + "slug": "زنگنه", + "province_id": 30 + }, + { + "id": 1071, + "name": "سامن", + "slug": "سامن", + "province_id": 30 + }, + { + "id": 1072, + "name": "سرکان", + "slug": "سرکان", + "province_id": 30 + }, + { + "id": 1073, + "name": "شیرین سو", + "slug": "شیرین-سو", + "province_id": 30 + }, + { + "id": 1074, + "name": "صالح آباد", + "slug": "همدان-صالح-آباد", + "province_id": 30 + }, + { + "id": 1075, + "name": "فامنین", + "slug": "فامنین", + "province_id": 30 + }, + { + "id": 1076, + "name": "فرسفج", + "slug": "فرسفج", + "province_id": 30 + }, + { + "id": 1077, + "name": "فیروزان", + "slug": "فیروزان", + "province_id": 30 + }, + { + "id": 1078, + "name": "قروه درجزین", + "slug": "قروه-درجزین", + "province_id": 30 + }, + { + "id": 1079, + "name": "قهاوند", + "slug": "قهاوند", + "province_id": 30 + }, + { + "id": 1080, + "name": "کبودر آهنگ", + "slug": "کبودر-آهنگ", + "province_id": 30 + }, + { + "id": 1081, + "name": "گل تپه", + "slug": "گل-تپه", + "province_id": 30 + }, + { + "id": 1082, + "name": "گیان", + "slug": "گیان", + "province_id": 30 + }, + { + "id": 1083, + "name": "لالجین", + "slug": "لالجین", + "province_id": 30 + }, + { + "id": 1084, + "name": "مریانج", + "slug": "مریانج", + "province_id": 30 + }, + { + "id": 1085, + "name": "ملایر", + "slug": "ملایر", + "province_id": 30 + }, + { + "id": 1086, + "name": "نهاوند", + "slug": "نهاوند", + "province_id": 30 + }, + { + "id": 1087, + "name": "همدان", + "slug": "همدان", + "province_id": 30 + }, + { + "id": 1088, + "name": "ابرکوه", + "slug": "ابرکوه", + "province_id": 31 + }, + { + "id": 1089, + "name": "احمدآباد", + "slug": "احمدآباد", + "province_id": 31 + }, + { + "id": 1090, + "name": "اردکان", + "slug": "یزد-اردکان", + "province_id": 31 + }, + { + "id": 1091, + "name": "اشکذر", + "slug": "اشکذر", + "province_id": 31 + }, + { + "id": 1092, + "name": "بافق", + "slug": "بافق", + "province_id": 31 + }, + { + "id": 1093, + "name": "بفروئیه", + "slug": "بفروئیه", + "province_id": 31 + }, + { + "id": 1094, + "name": "بهاباد", + "slug": "بهاباد", + "province_id": 31 + }, + { + "id": 1095, + "name": "تفت", + "slug": "تفت", + "province_id": 31 + }, + { + "id": 1096, + "name": "حمیدیا", + "slug": "حمیدیا", + "province_id": 31 + }, + { + "id": 1097, + "name": "خضرآباد", + "slug": "خضرآباد", + "province_id": 31 + }, + { + "id": 1098, + "name": "دیهوک", + "slug": "دیهوک", + "province_id": 31 + }, + { + "id": 1099, + "name": "زارچ", + "slug": "زارچ", + "province_id": 31 + }, + { + "id": 1100, + "name": "شاهدیه", + "slug": "شاهدیه", + "province_id": 31 + }, + { + "id": 1101, + "name": "طبس", + "slug": "یزد-طبس", + "province_id": 31 + }, + { + "id": 1103, + "name": "عقدا", + "slug": "عقدا", + "province_id": 31 + }, + { + "id": 1104, + "name": "مروست", + "slug": "مروست", + "province_id": 31 + }, + { + "id": 1105, + "name": "مهردشت", + "slug": "مهردشت", + "province_id": 31 + }, + { + "id": 1106, + "name": "مهریز", + "slug": "مهریز", + "province_id": 31 + }, + { + "id": 1107, + "name": "میبد", + "slug": "میبد", + "province_id": 31 + }, + { + "id": 1108, + "name": "ندوشن", + "slug": "ندوشن", + "province_id": 31 + }, + { + "id": 1109, + "name": "نیر", + "slug": "یزد-نیر", + "province_id": 31 + }, + { + "id": 1110, + "name": "هرات", + "slug": "هرات", + "province_id": 31 + }, + { + "id": 1111, + "name": "یزد", + "slug": "یزد", + "province_id": 31 + }, + { + "id": 1116, + "name": "پرند", + "slug": "پرند", + "province_id": 8 + }, + { + "id": 1117, + "name": "فردیس", + "slug": "فردیس", + "province_id": 5 + }, + { + "id": 1118, + "name": "مارلیک", + "slug": "مارلیک", + "province_id": 5 + }, + { + "id": 1119, + "name": "سادات شهر", + "slug": "سادات-شهر", + "province_id": 27 + }, + { + "id": 1121, + "name": "زیباکنار", + "slug": "زیباکنار", + "province_id": 26 + }, + { + "id": 1135, + "name": "کردان", + "slug": "کردان", + "province_id": 5 + }, + { + "id": 1137, + "name": "ساوجبلاغ", + "slug": "ساوجبلاغ", + "province_id": 5 + }, + { + "id": 1138, + "name": "تهران دشت", + "slug": "تهران-دشت", + "province_id": 5 + }, + { + "id": 1150, + "name": "گلبهار", + "slug": "گلبهار", + "province_id": 11 + }, + { + "id": 1153, + "name": "قیامدشت", + "slug": "قیامدشت", + "province_id": 8 + }, + { + "id": 1155, + "name": "بینالود", + "slug": "بینالود", + "province_id": 11 + }, + { + "id": 1159, + "name": "پیربازار", + "slug": "پیربازار", + "province_id": 26 + }, + { + "id": 1160, + "name": "رضوانشهر", + "slug": "رضوانشهر", + "province_id": 31 + } +] \ No newline at end of file diff --git a/src/static/states.json b/src/static/states.json new file mode 100644 index 0000000..99abfdb --- /dev/null +++ b/src/static/states.json @@ -0,0 +1,157 @@ +[ + { + "id": 1, + "name": "آذربایجان شرقی", + "slug": "آذربایجان-شرقی" + }, + { + "id": 2, + "name": "آذربایجان غربی", + "slug": "آذربایجان-غربی" + }, + { + "id": 3, + "name": "اردبیل", + "slug": "اردبیل" + }, + { + "id": 4, + "name": "اصفهان", + "slug": "اصفهان" + }, + { + "id": 5, + "name": "البرز", + "slug": "البرز" + }, + { + "id": 6, + "name": "ایلام", + "slug": "ایلام" + }, + { + "id": 7, + "name": "بوشهر", + "slug": "بوشهر" + }, + { + "id": 8, + "name": "تهران", + "slug": "تهران" + }, + { + "id": 9, + "name": "چهارمحال و بختیاری", + "slug": "چهارمحال-بختیاری" + }, + { + "id": 10, + "name": "خراسان جنوبی", + "slug": "خراسان-جنوبی" + }, + { + "id": 11, + "name": "خراسان رضوی", + "slug": "خراسان-رضوی" + }, + { + "id": 12, + "name": "خراسان شمالی", + "slug": "خراسان-شمالی" + }, + { + "id": 13, + "name": "خوزستان", + "slug": "خوزستان" + }, + { + "id": 14, + "name": "زنجان", + "slug": "زنجان" + }, + { + "id": 15, + "name": "سمنان", + "slug": "سمنان" + }, + { + "id": 16, + "name": "سیستان و بلوچستان", + "slug": "سیستان-بلوچستان" + }, + { + "id": 17, + "name": "فارس", + "slug": "فارس" + }, + { + "id": 18, + "name": "قزوین", + "slug": "قزوین" + }, + { + "id": 19, + "name": "قم", + "slug": "قم" + }, + { + "id": 20, + "name": "کردستان", + "slug": "کردستان" + }, + { + "id": 21, + "name": "کرمان", + "slug": "کرمان" + }, + { + "id": 22, + "name": "کرمانشاه", + "slug": "کرمانشاه" + }, + { + "id": 23, + "name": "کهگیلویه و بویراحمد", + "slug": "کهگیلویه-بویراحمد" + }, + { + "id": 24, + "name": "گلستان", + "slug": "گلستان" + }, + { + "id": 25, + "name": "لرستان", + "slug": "لرستان" + }, + { + "id": 26, + "name": "گیلان", + "slug": "گیلان" + }, + { + "id": 27, + "name": "مازندران", + "slug": "مازندران" + }, + { + "id": 28, + "name": "مرکزی", + "slug": "مرکزی" + }, + { + "id": 29, + "name": "هرمزگان", + "slug": "هرمزگان" + }, + { + "id": 30, + "name": "همدان", + "slug": "همدان" + }, + { + "id": 31, + "name": "یزد", + "slug": "یزد" + } +] \ No newline at end of file diff --git a/src/users/entities/db-service/damage-expert.db.service.ts b/src/users/entities/db-service/damage-expert.db.service.ts new file mode 100644 index 0000000..936e9e8 --- /dev/null +++ b/src/users/entities/db-service/damage-expert.db.service.ts @@ -0,0 +1,110 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, Types } from "mongoose"; +import { RegisterDto } from "src/auth/dto/actor/register.actor.dto"; +import { ExpertModel } from "src/users/entities/schema/expert.schema"; +import { DamageExpertModel } from "../schema/damage-expert.schema"; +import { UserModel } from "../schema/user.schema"; + +@Injectable() +export class DamageExpertDbService { + constructor( + @InjectModel(DamageExpertModel.name) + private readonly damageExpertModel: Model, + ) {} + + async create(user: RegisterDto): Promise { + return await this.damageExpertModel.create(user); + } + + async findOne(user: FilterQuery): Promise { + return await this.damageExpertModel.findOne(user); + } + + async findById(userId: string): Promise { + return await this.damageExpertModel.findOne({ + _id: new Types.ObjectId(userId), + }); + } + + async findOneAndUpdate( + user: FilterQuery, + update: FilterQuery, + ): Promise { + return await this.damageExpertModel.findOneAndUpdate(user, update); + } + + async findAll( + filter: FilterQuery, + ): Promise<(DamageExpertModel & { _id: Types.ObjectId })[]> { + return await this.damageExpertModel.find(filter).lean(); + } + + async findAndUpdate(filter: any, update: any): Promise { + return this.damageExpertModel + .findOneAndUpdate(filter, update, { new: true }) + .exec(); + } + + async updateStats( + expertId: string, + type: "checked" | "handled", + requestId: string, + ) { + if (!expertId || !requestId || !["checked", "handled"].includes(type)) { + console.warn("Invalid expertId, requestId, or type"); + return; + } + + const expert = await this.damageExpertModel.findById(expertId); + if (!expert) { + console.warn("Expert not found:", expertId); + return; + } + + const requestIdStr = new Types.ObjectId(requestId).toString(); + const countedRequests = + expert.countedRequests?.map((r) => r.toString()) || []; + + if (type === "checked" && countedRequests.includes(requestIdStr)) { + console.log( + `Request ${requestIdStr} already checked for expert ${expertId}`, + ); + return; + } + + const update: any = { $inc: {}, $push: {} }; + + if (type === "checked") { + update.$inc["requestStats.totalChecked"] = 1; + update.$push["countedRequests"] = requestIdStr; + } else if (type === "handled") { + update.$inc["requestStats.totalHandled"] = 1; + + if (countedRequests.includes(requestIdStr)) { + update.$inc["requestStats.totalChecked"] = -1; + } + + if (!countedRequests.includes(requestIdStr)) { + update.$push["countedRequests"] = requestIdStr; + } else { + delete update.$push; + } + } + + const result = await this.damageExpertModel.findByIdAndUpdate( + expertId, + update, + ); + + if (!result) { + console.warn("Failed to update stats for expert:", expertId); + } else { + console.log(`Stats updated (${type}) for expert:`, expertId); + } + } + + async updateOne(filter: any, update: any) { + return this.damageExpertModel.updateOne(filter, update); + } +} diff --git a/src/users/entities/db-service/expert.db.service.ts b/src/users/entities/db-service/expert.db.service.ts new file mode 100644 index 0000000..5b86a00 --- /dev/null +++ b/src/users/entities/db-service/expert.db.service.ts @@ -0,0 +1,83 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, Types } from "mongoose"; +import { RegisterDto } from "src/auth/dto/actor/register.actor.dto"; +import { ExpertModel } from "src/users/entities/schema/expert.schema"; +import { UserModel } from "../schema/user.schema"; + +@Injectable() +export class ExpertDbService { + constructor( + @InjectModel(ExpertModel.name) + private readonly expertModel: Model, + ) {} + + async create(user: RegisterDto): Promise { + return await this.expertModel.create(user); + } + + async findOne(user: FilterQuery): Promise { + return await this.expertModel.findOne(user).lean(); + } + + async findOneAndUpdate( + user: FilterQuery, + update: FilterQuery, + ): Promise { + return await this.expertModel.findOneAndUpdate(user, update); + } + + async findAll( + filter: FilterQuery, + ): Promise<(ExpertModel & { _id: Types.ObjectId })[]> { + return await this.expertModel.find(filter).lean(); + } + + async updateStats( + expertId: string, + type: "checked" | "handled", + requestId: string, + ) { + if (!expertId || !requestId || !["checked", "handled"].includes(type)) { + console.warn("Invalid expertId, requestId, or type"); + return; + } + + const expert = await this.expertModel.findById(expertId); + if (!expert) { + console.warn("Expert not found:", expertId); + return; + } + + const requestIdStr = new Types.ObjectId(requestId).toString(); + const counted = (expert.countedRequests || []).map((r) => r.toString()); + + if (counted.includes(requestIdStr)) { + console.log("Request already counted for expert:", requestIdStr); + return; + } + + const updateField = + type === "handled" + ? { + "requestStats.totalHandled": 1, + "requestStats.totalChecked": -1, + } + : { "requestStats.totalChecked": 1 }; + + const result = await this.expertModel.findByIdAndUpdate(expertId, { + $inc: updateField, + $push: { countedRequests: requestIdStr }, + }); + + if (!result) { + console.warn("Failed to update expert stats for:", expertId); + } else { + console.log("Updated stats for expert:", expertId); + } + } + + async updateOne(filter: any, update: any) { + return this.expertModel.updateOne(filter, update); + } +} diff --git a/src/users/entities/db-service/insurer-expert.db.service.ts b/src/users/entities/db-service/insurer-expert.db.service.ts new file mode 100644 index 0000000..55cb68c --- /dev/null +++ b/src/users/entities/db-service/insurer-expert.db.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model } from "mongoose"; +import { InsurerExpertModel } from "../schema/insurer-expert.schema"; + +@Injectable() +export class InsurerExpertDbService { + constructor( + @InjectModel(InsurerExpertModel.name) + private readonly insurerExpert: Model, + ) {} + + async create(insurerExpert: any) { + return await this.insurerExpert.create(insurerExpert); + } + + async findOne(filter: FilterQuery) { + return await this.insurerExpert.findOne(filter); + } +} diff --git a/src/users/entities/db-service/user.db.service.ts b/src/users/entities/db-service/user.db.service.ts new file mode 100644 index 0000000..f610135 --- /dev/null +++ b/src/users/entities/db-service/user.db.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { FilterQuery, Model, UpdateQuery } from "mongoose"; +import { UserDocument, UserModel } from "../schema/user.schema"; + +@Injectable() +export class UserDbService { + constructor( + @InjectModel(UserModel.name) private readonly userModel: Model, + ) {} + + async createUser(user: UserModel): Promise { + return await this.userModel.create(user); + } + + async findOne(user: FilterQuery): Promise { + return await this.userModel.findOne(user); + } + + async findPlate(plate: number) { + return await this.userModel.find({ "plates.leftDigits": { $in: plate } }); + } + + async findOneAndUpdate( + user: FilterQuery, + update: UpdateQuery, + ): Promise { + return await this.userModel.findOneAndUpdate(user, update); + } +} diff --git a/src/users/entities/schema/damage-expert.schema.ts b/src/users/entities/schema/damage-expert.schema.ts new file mode 100644 index 0000000..d70a392 --- /dev/null +++ b/src/users/entities/schema/damage-expert.schema.ts @@ -0,0 +1,197 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { + ExpertizedAtEnum, + PreviousWorkEnum, + SkillEnum, +} from "src/Types&Enums/damage-expert.enum"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { UserType } from "src/Types&Enums/userType.enum"; + +@Schema({ _id: false }) +export class RequestStats { + @Prop({ default: 0 }) + totalHandled: number; + + @Prop({ default: 0 }) + totalChecked: number; +} + +const RequestStatsSchema = SchemaFactory.createForClass(RequestStats); + +@Schema({ _id: false }) +export class PersonalInfo { + @Prop({ type: "string" }) + fullName: string; + + @Prop({ type: "string" }) + nationalCode: string; + + @Prop({ type: "string" }) + dob: string; + + @Prop({ type: "string" }) + mobile: string; +} + +const PersonalInfoSchema = SchemaFactory.createForClass(PersonalInfo); + +@Schema({ _id: false }) +export class PersonalDocs { + @Prop({ type: "string" }) + nationalCard?: string; + + @Prop({ type: "string" }) + selfie?: string; + + @Prop({ type: "string" }) + activityCert?: string; +} + +const PersonalDocsSchema = SchemaFactory.createForClass(PersonalDocs); + +@Schema({ _id: false }) +export class WorkExperience { + @Prop({ type: Number }) + state: number; + + @Prop({ type: Number }) + city: number; + + @Prop({ + type: [{ type: String, enum: ExpertizedAtEnum }], + default: [], + }) + expertizedAt: ExpertizedAtEnum[]; +} + +const WorkExperienceSchema = SchemaFactory.createForClass(WorkExperience); + +@Schema({ _id: false }) +export class WorkRecords { + @Prop({ type: Number }) + workingYears: number; + + @Prop({ type: Number }) + casesCount: number; + + @Prop({ type: String, enum: PreviousWorkEnum }) + previousWork: PreviousWorkEnum; +} + +const WorkRecordsSchema = SchemaFactory.createForClass(WorkRecords); + +@Schema({ _id: false }) +export class FinancialInfo { + @Prop({ type: "string" }) + IBAN: string; + + @Prop({ type: "string" }) + cardNum: string; + + @Prop({ type: "string" }) + accountNum: string; +} + +const FinancialInfoSchema = SchemaFactory.createForClass(FinancialInfo); + +@Schema({ collection: "damage-expert", versionKey: false, timestamps: true }) +export class DamageExpertModel { + @Prop({ required: true }) + userType: UserType; + + @Prop({ required: true }) + firstName: string; + + @Prop({ required: true }) + role: RoleEnum; + + @Prop({ required: true }) + lastName: string; + + @Prop({ unique: true }) + nationalCode: string; + + @Prop({ type: "string", unique: true }) + email: string; + + @Prop({ type: "string" }) + username: string; + + @Prop({ required: true }) + password: string; + + @Prop({ required: true }) + mobile: string; + + @Prop({ required: false }) + insuActivityCo?: string | null; + + @Prop({ type: Types.ObjectId }) + clientKey?: Types.ObjectId; + + @Prop({ type: PersonalInfoSchema }) + personalInfo?: PersonalInfo; + + @Prop({ type: PersonalDocsSchema }) + personalDocs?: PersonalDocs; + + @Prop({ type: WorkExperienceSchema }) + workExperience?: WorkExperience; + + @Prop({ type: WorkRecordsSchema }) + workRecords?: WorkRecords; + + @Prop({ + type: [{ type: String, enum: SkillEnum }], + default: [], + }) + skills?: SkillEnum[]; + + @Prop({ type: FinancialInfoSchema }) + financialInfo?: FinancialInfo; + + @Prop({ type: "string" }) + otp: string; + + @Prop({ type: "string" }) + address: string; + + @Prop({ type: "string" }) + state: string; + + @Prop({ type: "string" }) + city: string; + + @Prop({ type: RequestStatsSchema, default: () => ({}) }) + requestStats: RequestStats; + + @Prop({ + type: [{ type: String }], + default: [], + validate: { + validator: (arr: any[]) => arr.every((val) => typeof val === "string"), + message: "All countedRequests must be strings", + }, + }) + countedRequests?: string[]; + + createdAt: Date; +} + +export const DamageExpertDbSchema = + SchemaFactory.createForClass(DamageExpertModel); + +DamageExpertDbSchema.pre("save", function (next) { + if (!this.username) { + this.username = this.email; + } + next(); +}); + +DamageExpertDbSchema.pre("save", function (next) { + if (!this.requestStats) { + this.requestStats = { totalChecked: 0, totalHandled: 0 }; + } + next(); +}); diff --git a/src/users/entities/schema/expert.schema.ts b/src/users/entities/schema/expert.schema.ts new file mode 100644 index 0000000..31f6589 --- /dev/null +++ b/src/users/entities/schema/expert.schema.ts @@ -0,0 +1,105 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Degrees } from "src/Types&Enums/degrees.enum"; +import { RoleEnum } from "src/Types&Enums/role.enum"; +import { UserType } from "src/Types&Enums/userType.enum"; + +@Schema({ _id: false }) +export class RequestStats { + @Prop({ default: 0 }) + totalHandled: number; + + @Prop({ default: 0 }) + totalChecked: number; +} + +const RequestStatsSchema = SchemaFactory.createForClass(RequestStats); + +@Schema({ collection: "expert", versionKey: false, timestamps: true }) +export class ExpertModel { + @Prop({}) + firstName: string; + + @Prop({}) + role: RoleEnum; + + @Prop({}) + userType: UserType; + + @Prop({}) + lastName: string; + + @Prop({ unique: true }) + nationalCode: string; + + @Prop({ type: "string", unique: true }) + email: string; + + @Prop({}) + password: string; + + @Prop({}) + sheba: string; + + @Prop({}) + phone: string; + + @Prop({}) + mobile: string; + + @Prop({}) + city: string; + + @Prop({}) + state: string; + + @Prop({}) + address: string; + + @Prop({ type: String }) + expDegree: Degrees; + + @Prop({}) + insuActivityTime?: number; + + @Prop({}) + insuActivityCo?: string; + + @Prop({}) + policeActivityTime?: number; + + @Prop({}) + policeActivityKind?: string; + + @Prop({}) + policeServicePlace?: string; + + @Prop({}) + clientKey?: string; + + @Prop({ type: "string" }) + otp: string; + + @Prop({ type: RequestStatsSchema, default: () => ({}) }) + requestStats: RequestStats; + + @Prop({ + type: [{ type: String }], + default: [], + validate: { + validator: (arr: any[]) => arr.every((val) => typeof val === "string"), + message: "All countedRequests must be strings", + }, + }) + countedRequests?: string[]; + + createdAt: Date; +} + +export const ExpertDbSchema = SchemaFactory.createForClass(ExpertModel); + +ExpertDbSchema.pre("save", function (next) { + if (!this.requestStats) { + this.requestStats = { totalChecked: 0, totalHandled: 0 }; + } + next(); +}); diff --git a/src/users/entities/schema/insurer-expert.schema.ts b/src/users/entities/schema/insurer-expert.schema.ts new file mode 100644 index 0000000..270bacb --- /dev/null +++ b/src/users/entities/schema/insurer-expert.schema.ts @@ -0,0 +1,32 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { RoleEnum } from "src/Types&Enums/role.enum"; + +@Schema({ + collection: "insurer-expert", + versionKey: false, + timestamps: true, +}) +export class InsurerExpertModel { + @Prop() + firstName: string; + + @Prop() + lastName: string; + + @Prop({ unique: true, required: true }) + email: string; + + @Prop() + password: string; + + @Prop() + role: RoleEnum; + + @Prop() + clientKey: Types.ObjectId; + + createdAt: Date; +} +export const InsurerExpertDbSchema = + SchemaFactory.createForClass(InsurerExpertModel); diff --git a/src/users/entities/schema/user.schema.ts b/src/users/entities/schema/user.schema.ts new file mode 100644 index 0000000..3ad9a7a --- /dev/null +++ b/src/users/entities/schema/user.schema.ts @@ -0,0 +1,57 @@ +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { Types } from "mongoose"; +import { Plates } from "src/Types&Enums/plate.interface"; + +export type UserDocument = UserModel & Document; + +@Schema({ collection: "user", versionKey: false }) +export class UserModel { + @Prop({ type: String, unique: true }) + username: string; + + @Prop({}) + otp: string; + + @Prop({ default: 0 }) + otpExpire: number | null; + + @Prop({ type: Object }) + tokens: { + token: string; + rfToken: string; + }; + + @Prop() + fullName: string; + + @Prop({ type: String }) + mobile: string; + + @Prop({ unique: false }) + nationalCode: string; + + @Prop({ type: Array }) + plates?: Plates[]; + + @Prop() + lastLogin: Date; + + @Prop() + clientKey: Types.ObjectId; + + @Prop() + birthDay: string; + + @Prop() + gender?: "male" | "female"; + + @Prop() + city: string; + + @Prop() + address: string; + + @Prop() + state: string; +} +export const UserDbSchema = SchemaFactory.createForClass(UserModel); diff --git a/src/users/users.module.ts b/src/users/users.module.ts new file mode 100644 index 0000000..85a2e8f --- /dev/null +++ b/src/users/users.module.ts @@ -0,0 +1,46 @@ +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service"; +import { ExpertModel } from "src/users/entities/schema/expert.schema"; +import { HashModule } from "src/utils/hash/hash.module"; +import { OtpModule } from "src/utils/otp/otp.module"; +import { ExpertDbService } from "./entities/db-service/expert.db.service"; +import { InsurerExpertDbService } from "./entities/db-service/insurer-expert.db.service"; +import { UserDbService } from "./entities/db-service/user.db.service"; +import { + DamageExpertDbSchema, + DamageExpertModel, +} from "./entities/schema/damage-expert.schema"; +import { ExpertDbSchema } from "./entities/schema/expert.schema"; +import { + InsurerExpertDbSchema, + InsurerExpertModel, +} from "./entities/schema/insurer-expert.schema"; +import { UserModel, UserDbSchema } from "./entities/schema/user.schema"; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { name: UserModel.name, schema: UserDbSchema }, + { name: DamageExpertModel.name, schema: DamageExpertDbSchema }, + { name: ExpertModel.name, schema: ExpertDbSchema }, + { name: InsurerExpertModel.name, schema: InsurerExpertDbSchema }, + ]), + OtpModule, + HashModule, + ], + controllers: [], + providers: [ + UserDbService, + ExpertDbService, + DamageExpertDbService, + InsurerExpertDbService, + ], + exports: [ + UserDbService, + ExpertDbService, + DamageExpertDbService, + InsurerExpertDbService, + ], +}) +export class UsersModule {} diff --git a/src/utils/cron/cron.module.ts b/src/utils/cron/cron.module.ts new file mode 100644 index 0000000..165c678 --- /dev/null +++ b/src/utils/cron/cron.module.ts @@ -0,0 +1,10 @@ +import { forwardRef, Module } from "@nestjs/common"; +import { RequestManagementModule } from "src/request-management/request-management.module"; +import { AutoCloseRequestService } from "./cron.service"; + +@Module({ + imports: [forwardRef(() => RequestManagementModule)], + providers: [AutoCloseRequestService], + exports: [AutoCloseRequestService], +}) +export class CronModule {} diff --git a/src/utils/cron/cron.service.ts b/src/utils/cron/cron.service.ts new file mode 100644 index 0000000..d849bed --- /dev/null +++ b/src/utils/cron/cron.service.ts @@ -0,0 +1,95 @@ +import { Injectable, Logger } from "@nestjs/common"; +import { Cron, CronExpression } from "@nestjs/schedule"; +import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; +import { SubmitReply } from "src/request-management/entities/schema/request-management.schema"; +import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; + +@Injectable() +export class AutoCloseRequestService { + private readonly logger = new Logger(AutoCloseRequestService.name); + + constructor(private readonly requestDb: RequestManagementDbService) {} + + async scheduleAutoClose(params: { requestId: string; runAt: Date }) { + await this.requestDb.findAndUpdate( + { _id: params.requestId }, + { $set: { autoCloseTriggerTime: params.runAt } }, + ); + this.logger.log( + `Scheduled auto-close for ${params.requestId} at ${params.runAt.toISOString()}`, + ); + } + + // Runs every hour + @Cron(CronExpression.EVERY_HOUR) + async handleAutoClose() { + const now = new Date(); + const threshold = new Date(now.getTime() - 24 * 60 * 60 * 1000); + + const requestsToClose = await this.requestDb.findAll({ + blameStatus: { $ne: ReqBlameStatus.CloseRequest }, + $or: [ + { "expertSubmitReply.firstPartyComment.isAccept": true }, + { "expertSubmitReply.secondPartyComment.isAccept": true }, + { "expertSubmitReplyFinal.firstPartyComment.isAccept": true }, + { "expertSubmitReplyFinal.secondPartyComment.isAccept": true }, + ], + autoCloseTriggerTime: { $lte: threshold }, + }); + + for (const request of requestsToClose) { + const reply = request.expertSubmitReplyFinal || request.expertSubmitReply; + + const isSubmitReply = (reply: any): reply is SubmitReply => + reply && + typeof reply === "object" && + ("firstPartyComment" in reply || "secondPartyComment" in reply); + + if (isSubmitReply(reply)) { + const firstAccepted = reply.firstPartyComment?.isAccept; + const secondAccepted = reply.secondPartyComment?.isAccept; + + if (!firstAccepted || !secondAccepted) { + await this.requestDb.findByIdAndUpdate(request._id.toString(), { + blameStatus: ReqBlameStatus.CloseRequest, + }); + this.logger.log(`Auto-closed request ${request._id}`); + } + } + } + } + + @Cron(CronExpression.EVERY_DAY_AT_2AM) + async handleBlameFileCleanup() { + this.logger.log("Running scheduled blame file cleanup job..."); + + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + + const staleStatuses = [ + ReqBlameStatus.PendingForFirstParty, + ReqBlameStatus.PendingForSecondParty, + ]; + + const query = { + createdAt: { $lt: sevenDaysAgo }, + blameStatus: { $in: staleStatuses }, + }; + + try { + const result = await this.requestDb.updateMany(query, { + $set: { blameStatus: ReqBlameStatus.CloseRequest }, + }); + + if (result.modifiedCount > 0) { + this.logger.log( + `Successfully closed ${result.modifiedCount} stale blame files.`, + ); + } else { + this.logger.log("No stale blame files found to close."); + } + } catch (error) { + this.logger.error("Error during scheduled blame file cleanup:", error); + } + } +} diff --git a/src/utils/hash/hash.module.ts b/src/utils/hash/hash.module.ts new file mode 100644 index 0000000..d300f53 --- /dev/null +++ b/src/utils/hash/hash.module.ts @@ -0,0 +1,9 @@ +import { Module } from "@nestjs/common"; +import { HashService } from "./hash.service"; + +@Module({ + imports: [], + providers: [HashService], + exports: [HashService], +}) +export class HashModule {} diff --git a/src/utils/hash/hash.service.ts b/src/utils/hash/hash.service.ts new file mode 100644 index 0000000..8e71046 --- /dev/null +++ b/src/utils/hash/hash.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from "@nestjs/common"; +import * as bcrypt from "bcrypt"; + +@Injectable() +export class HashService { + async hash(password: string): Promise { + const salt = await bcrypt.genSalt(10); + return bcrypt.hash(password, salt); + } + + async compare(password: string, hash: string) { + return bcrypt.compare(password, hash); + } +} diff --git a/src/utils/mail/mail.module.ts b/src/utils/mail/mail.module.ts new file mode 100644 index 0000000..83d07c5 --- /dev/null +++ b/src/utils/mail/mail.module.ts @@ -0,0 +1,26 @@ +import { Module } from "@nestjs/common"; +import { MailerModule } from "@nestjs-modules/mailer"; +import { MailService } from "./mail.service"; + +@Module({ + providers: [MailService], + exports: [MailService], + imports: [ + MailerModule.forRoot({ + transport: { + service: "gmail", + host: "smtp.gmail.com", + port: 465, + secure: true, + auth: { + user: "balali.arash@gmail.com", + pass: "macujakosnsqbgdm", + }, + }, + defaults: { + from: "KSG ", + }, + }), + ], +}) +export class MailModule {} diff --git a/src/utils/mail/mail.service.ts b/src/utils/mail/mail.service.ts new file mode 100644 index 0000000..8835591 --- /dev/null +++ b/src/utils/mail/mail.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from "@nestjs/common"; +import { MailerService } from "@nestjs-modules/mailer"; + +@Injectable() +export class MailService { + constructor(private readonly mailerService: MailerService) {} + async sendMail(email, otp: string): Promise { + let status: object; + await this.mailerService + .sendMail({ + to: email, + from: "noreply@yara724.com", + subject: "YARA724 Company", + text: "welcome", + html: ` +

yara724 verificateion

+

+
+ ${otp} +
+

+ `, + }) + .then((success) => { + if (success) { + status = { + message: "email was sent successfully", + code: 200, + emailSent: true, + }; + } + }) + .catch((err) => { + if (err) { + status = { message: "please try again", code: 500, emailSent: false }; + } + }); + + return status; + } +} diff --git a/src/utils/otp/otp.module.ts b/src/utils/otp/otp.module.ts new file mode 100644 index 0000000..ab986ca --- /dev/null +++ b/src/utils/otp/otp.module.ts @@ -0,0 +1,8 @@ +import { Module } from "@nestjs/common"; +import { OtpService } from "./otp.service"; + +@Module({ + providers: [OtpService], + exports: [OtpService], +}) +export class OtpModule {} diff --git a/src/utils/otp/otp.service.ts b/src/utils/otp/otp.service.ts new file mode 100644 index 0000000..70ef6df --- /dev/null +++ b/src/utils/otp/otp.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from "@nestjs/common"; +// import crypto from "crypto" +const crypto = require("node:crypto"); + +@Injectable() +export class OtpService { + createDigit(digits: number): string { + const max = Math.pow(10, digits); + const randomBytes = crypto.randomBytes(Math.ceil(digits / 2)); + const randomNumber = parseInt(randomBytes.toString("hex"), 16) % max; + return randomNumber.toString().padStart(digits, "0"); + } + create() { + return this.createDigit(5); + } +} diff --git a/src/utils/pipes/parse-json.pipe.ts b/src/utils/pipes/parse-json.pipe.ts new file mode 100644 index 0000000..8c71a52 --- /dev/null +++ b/src/utils/pipes/parse-json.pipe.ts @@ -0,0 +1,29 @@ +import { + PipeTransform, + Injectable, + BadRequestException, + ArgumentMetadata, +} from "@nestjs/common"; + +@Injectable() +export class ParseJsonPipe implements PipeTransform { + transform(value: string | undefined, metadata: ArgumentMetadata): any { + if (value === undefined) { + return undefined; + } + + if (typeof value !== "string") { + throw new BadRequestException( + `Validation failed: Expected a stringified JSON for query parameter '${metadata.data}'.`, + ); + } + + try { + return JSON.parse(value); + } catch (e) { + throw new BadRequestException( + `Invalid JSON format in query parameter '${metadata.data}'.`, + ); + } + } +} diff --git a/src/utils/sms-manager/sms-manager.module.ts b/src/utils/sms-manager/sms-manager.module.ts new file mode 100644 index 0000000..90b0c52 --- /dev/null +++ b/src/utils/sms-manager/sms-manager.module.ts @@ -0,0 +1,15 @@ +import { KavenegarModule } from "@fraybabak/kavenegar_nest"; +import { Module } from "@nestjs/common"; +import { SmsManagerService } from "./sms-manager.service"; + +@Module({ + imports: [ + KavenegarModule.forRoot({ + apikey: + "75776C717969412B4B52306A5956462F4A714E6F6C65544D6A2B654B7566786E", + }), + ], + providers: [SmsManagerService], + exports: [SmsManagerService], +}) +export class SmsManagerModule {} diff --git a/src/utils/sms-manager/sms-manager.service.ts b/src/utils/sms-manager/sms-manager.service.ts new file mode 100644 index 0000000..6dd8b3a --- /dev/null +++ b/src/utils/sms-manager/sms-manager.service.ts @@ -0,0 +1,30 @@ +import { KavenegarService } from "@fraybabak/kavenegar_nest"; +import { Injectable } from "@nestjs/common"; + +export interface SendMessage { + message: string; + // sender: string; + receptor: string; +} + +export interface VerifyLookUpMessage { + template: string; + token: string; + receptor: string; +} + +@Injectable() +export class SmsManagerService { + constructor(private readonly sender: KavenegarService) {} + + async sendMessage(data: SendMessage) { + return await this.sender.Send({ + sender: "10008663", + ...data, + }); + } + + async verifyLookUp(data: VerifyLookUpMessage) { + return await this.sender.verifyLookup(data); + } +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..64f86c6 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1294229 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES6", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "allowJs": false, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false + } +} \ No newline at end of file