diff --git a/package-lock.json b/package-lock.json index ca7663e..ef29116 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,9 +29,11 @@ "pdfkit": "^0.19.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", + "socks-proxy-agent": "^8.0.4", "svg-captcha": "^1.4.0" }, "devDependencies": { + "@compodoc/compodoc": "^2.0.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.18.0", "@nestjs/cli": "^11.0.0", @@ -64,6 +66,75 @@ "npm": ">=10.0.0" } }, + "node_modules/@aduh95/viz.js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.4.0.tgz", + "integrity": "sha512-KI2nVf9JdwWCXqK6RVf+9/096G7VWN4Z84mnynlyZKao2xQENW8WNEjLmvdlxS5X8PNWXFC1zqwm7tveOXw/4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core": { + "version": "22.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-22.0.4.tgz", + "integrity": "sha512-zA2UJSMAU3su5uJTOn5ul/gCLRcw6/uIQ6EC5v/Ju/ePjgDIw9R3y3MAvWQ4Ibi/fXiq0FVxpF8hE7RUclYmJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.20.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.2", + "source-map": "0.7.6" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^5.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "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/@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/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/@angular-devkit/schematics": { "version": "19.2.24", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.24.tgz", @@ -260,6 +331,16 @@ "tslib": "^2.1.0" } }, + "node_modules/@arr/every": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@arr/every/-/every-1.0.1.tgz", + "integrity": "sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -797,6 +878,369 @@ "node": ">=0.1.90" } }, + "node_modules/@compodoc/compodoc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-2.0.0.tgz", + "integrity": "sha512-+6/IlEoprDXlFEjZD/kf1AFqwQSvH8H0cJD6gHgGfB+b7yapH7XBCKBeLQ6/994XTdYCpBtxIdfasyIZM9fnrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/schematics": "22.0.4", + "@compodoc/ngd-transformer": "^2.1.3", + "body-parser": "^2.3.0", + "bootstrap.native": "^5.1.10", + "cheerio": "1.2.0", + "chokidar": "^5.0.0", + "commander": "^15.0.0", + "cosmiconfig": "^9.0.2", + "es6-shim": "^0.35.8", + "fs-extra": "^11.3.5", + "handlebars": "^4.7.9", + "html-entities": "^2.6.0", + "i18next": "26.3.3", + "json5": "^2.2.3", + "loglevel": "^1.9.2", + "loglevel-plugin-prefix": "^0.8.4", + "lunr": "^2.3.9", + "marked": "18.0.5", + "neotraverse": "^1.0.1", + "picocolors": "^1.1.1", + "polka": "^0.5.2", + "prismjs": "^1.30.0", + "semver": "^7.8.5", + "sirv": "^3.0.2", + "svg-pan-zoom": "^3.6.2", + "tablesort": "^5.7.1", + "tinyglobby": "^0.2.17", + "ts-morph": "^28.0.0", + "uuid": "14.0.1", + "vis-network": "^10.1.0" + }, + "bin": { + "compodoc": "bin/index-cli.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/compodoc" + } + }, + "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/schematics": { + "version": "22.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-22.0.4.tgz", + "integrity": "sha512-VRxL1hD/Q3TQglM6EfQ0ksAW4OIvtKyZgtaUpyGsJRfD6tGmLFn7MDnmyyq1ceLX/Clq+3tzH/wN0tF6rcE0jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "22.0.4", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.21", + "ora": "9.4.0", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@compodoc/compodoc/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@compodoc/compodoc/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "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/@compodoc/compodoc/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/cli-spinners": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", + "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@compodoc/compodoc/node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@compodoc/compodoc/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@compodoc/compodoc/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@compodoc/compodoc/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/ora": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.0.tgz", + "integrity": "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.2.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", + "stdin-discarder": "^0.3.2", + "string-width": "^8.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/compodoc/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@compodoc/ngd-core": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.1.tgz", + "integrity": "sha512-Z+wE6wWZYVnudRYg6qunDlyh3Orw39Ib66Gvrz5kX5u7So+iu3tr6sQJdqH6yGS3hAjig5avlfhWLlgsb6/x1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "fancy-log": "^2.0.0", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@compodoc/ngd-transformer": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.3.tgz", + "integrity": "sha512-oWxJza7CpWR8/FeWYfE6j+jgncnGBsTWnZLt5rD2GUpsGSQTuGrsFPnmbbaVLgRS5QIVWBJYke7QFBr/7qVMWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@aduh95/viz.js": "3.4.0", + "@compodoc/ngd-core": "~2.1.1", + "dot": "^2.0.0-beta.1", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@compodoc/ngd-transformer/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "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", @@ -821,6 +1265,20 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/hammerjs": "^2.0.36" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -3007,6 +3465,13 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@polka/url": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz", + "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==", + "dev": true, + "license": "MIT" + }, "node_modules/@scarf/scarf": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", @@ -3422,6 +3887,42 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@thednp/event-listener": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.15.tgz", + "integrity": "sha512-X1B+Yuvb30a/0rGdXnTLMfqvzCQ4D/1Kns7B78ON3BoFNFazJyFQ4ypuErY1jQLZSFDN4c3zTAIrdrtuIa/fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, + "node_modules/@thednp/position-observer": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@thednp/position-observer/-/position-observer-1.1.3.tgz", + "integrity": "sha512-5I3rdcZGTd6xJrDG0uRgME6O4jeYHlt2bedXz2yTkkCttYHhDuhtkTh3a2+NzMv2yNPXpXz0HykkeEm3RgQDDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@thednp/shorty": "^2.0.14" + }, + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, + "node_modules/@thednp/shorty": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.14.tgz", + "integrity": "sha512-jCxbkyQryQjovIzK5ko683RNq1pZRAKiscJfhtF71yGxneEzFR9IQgzayw/WRjQ5KDD/5UVqTwgtfQ7pJpmIYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, "node_modules/@tokenizer/inflate": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", @@ -3445,6 +3946,57 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, + "node_modules/@ts-morph/common": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.29.0.tgz", + "integrity": "sha512-35oUmphHbJvQ/+UTwFNme/t2p3FoKiGJ5auTjjpNTop2dyREspirjMy82PLSC1pnDJ8ah1GU98hwpVt64YXQsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.14" + } + }, + "node_modules/@ts-morph/common/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -3610,6 +4162,14 @@ "@types/node": "*" } }, + "node_modules/@types/hammerjs": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -5347,20 +5907,20 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -5370,6 +5930,42 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/bootstrap.native": { + "version": "5.1.10", + "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.1.10.tgz", + "integrity": "sha512-JwJnMWx/zJirvwEPuvmKve4JoSh6upA5qNWdWj78nJmBCmVhBGXdYHxQIldkIRIReZjCiXntZBLFd3TYoGfgxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@thednp/event-listener": "^2.0.15", + "@thednp/position-observer": "^1.1.3", + "@thednp/shorty": "^2.0.14" + }, + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -5731,6 +6327,66 @@ "dev": true, "license": "MIT" }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -5887,6 +6543,13 @@ "node": ">= 0.12.0" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true, + "license": "MIT" + }, "node_modules/collect-v8-coverage": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", @@ -5914,6 +6577,16 @@ "dev": true, "license": "MIT" }, + "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==", + "dev": true, + "license": "ISC", + "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", @@ -6146,6 +6819,36 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "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.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -6276,6 +6979,72 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "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/dot": { + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", + "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", + "dev": true, + "license": "MIT" + }, "node_modules/dotenv": { "version": "17.4.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", @@ -6380,6 +7149,33 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", @@ -6394,6 +7190,29 @@ "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==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -6456,6 +7275,13 @@ "node": ">= 0.4" } }, + "node_modules/es6-shim": { + "version": "0.35.8", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", + "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", + "dev": true, + "license": "MIT" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -6856,6 +7682,19 @@ "node": ">=4" } }, + "node_modules/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-support": "^1.1.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7372,6 +8211,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -7672,6 +8524,23 @@ "node": ">= 0.4" } }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7679,6 +8548,39 @@ "dev": true, "license": "MIT" }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -7743,6 +8645,35 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "26.3.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.3.tgz", + "integrity": "sha512-aYVegyBdXSO93CMMihvr47jI7GHSOcIahMpJX+qzUXDzW4xDJf2uenIA+45vDU+YhiVdcfsql70AC9RVdMNrHg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -7864,6 +8795,15 @@ "kind-of": "^6.0.2" } }, + "node_modules/ip-address": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz", + "integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8928,6 +9868,14 @@ "node": ">=12.0.0" } }, + "node_modules/keycharm": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz", + "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==", + "dev": true, + "license": "(Apache-2.0 OR MIT)", + "peer": true + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -9142,6 +10090,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "dev": true, + "license": "MIT" + }, "node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -9165,6 +10134,13 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -9239,6 +10215,32 @@ "tmpl": "1.0.5" } }, + "node_modules/marked": { + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/matchit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/matchit/-/matchit-1.1.0.tgz", + "integrity": "sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@arr/every": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -9380,6 +10382,19 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", @@ -9525,6 +10540,16 @@ "node": ">=14.0.0" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9626,6 +10651,16 @@ "dev": true, "license": "MIT" }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -9696,6 +10731,19 @@ "node": ">=8" } }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "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", @@ -9940,6 +10988,59 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -9949,6 +11050,13 @@ "node": ">= 0.8" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -10181,6 +11289,17 @@ "browserify-zlib": "^0.2.0" } }, + "node_modules/polka": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/polka/-/polka-0.5.2.tgz", + "integrity": "sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^0.5.0", + "trouter": "^2.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10264,6 +11383,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -10399,6 +11528,20 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -10635,9 +11778,9 @@ } }, "node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10840,6 +11983,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sirv/node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -10857,6 +12022,53 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -10962,6 +12174,19 @@ "node": ">= 0.8" } }, + "node_modules/stdin-discarder": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", + "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -11185,6 +12410,13 @@ "node": ">=4.x" } }, + "node_modules/svg-pan-zoom": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.2.tgz", + "integrity": "sha512-JwnvRWfVKw/Xzfe6jriFyfey/lWJLq4bUh2jwoR5ChWQuQoOH8FEh1l/bEp46iHHKHEJWIyFJETbazraxNWECg==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/swagger-ui-dist": { "version": "5.32.6", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.6.tgz", @@ -11233,6 +12465,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tablesort": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.7.1.tgz", + "integrity": "sha512-wm+xSponmlN1spiuwGlrvNGoG8fEso+y86OOjS+Pl0Qsje/cOVSfUvELcXILWc8/sgEdY/NGMPo0qhFGyYKnwQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 22", + "npm": ">= 10" + } + }, "node_modules/tapable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", @@ -11529,9 +12772,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -11592,6 +12835,16 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tr46": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", @@ -11604,6 +12857,19 @@ "node": ">=18" } }, + "node_modules/trouter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/trouter/-/trouter-2.0.1.tgz", + "integrity": "sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "matchit": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -11704,6 +12970,17 @@ "webpack": "^5.0.0" } }, + "node_modules/ts-morph": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-28.0.0.tgz", + "integrity": "sha512-Wp3tnZ2bzwxyTZMtgWVzXDfm7lB1Drz+y9DmmYH/L702PQhPyVrp3pkou3yIz4qjS14GY9kcpmLiOOMvl8oG1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.29.0", + "code-block-writer": "^13.0.3" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -11955,6 +13232,16 @@ "through": "^2.3.8" } }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -12066,6 +13353,20 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/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", @@ -12106,6 +13407,60 @@ "node": ">= 0.8" } }, + "node_modules/vis-data": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-8.0.5.tgz", + "integrity": "sha512-tRQKcTGaclo60rTY1j4v7BgD9v5jfD3wl+JgR1q6I4AI7go3QH5OjMCLIuZPpL2sf4IRMlwyPlRLnK4m+Phjsw==", + "dev": true, + "license": "(Apache-2.0 OR MIT)", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/visjs" + }, + "peerDependencies": { + "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^13.0.0 || ^14.0.0", + "vis-util": ">=6.0.0" + } + }, + "node_modules/vis-network": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-10.1.2.tgz", + "integrity": "sha512-1Kj7GD7XNjygliqRWXv/VPT1X/2eKz2e09PvwDtETHwuvKJ0emid7/PmWoFqCMypun8rLbjgmalSfPkiVZ8ZMg==", + "dev": true, + "license": "(Apache-2.0 OR MIT)", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/visjs" + }, + "peerDependencies": { + "@egjs/hammerjs": "^2.0.0", + "component-emitter": "^1.3.0 || ^2.0.0", + "keycharm": "^0.2.0 || ^0.3.0 || ^0.4.0", + "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^13.0.0 || ^14.0.0", + "vis-data": ">=8.0.0", + "vis-util": ">=6.0.0" + } + }, + "node_modules/vis-util": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-6.0.2.tgz", + "integrity": "sha512-sYawqWllCqwmTPLurRgPChmfRppbWI/XoBWdGNM6N2s4XuVR8VO8gtz/v9AvrAAOfZxel/U5UBNOwyhFn0qb7A==", + "dev": true, + "license": "(Apache-2.0 OR MIT)", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/visjs" + }, + "peerDependencies": { + "@egjs/hammerjs": "^2.0.0", + "component-emitter": "^1.3.0 || ^2.0.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -12343,6 +13698,43 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", diff --git a/package.json b/package.json index bd449eb..93fa772 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "seed:parsian-tehran": "ts-node scripts/seed-parsian-tehran.ts", + "seed:reports-fixtures": "ts-node scripts/seed-insurer-reports-fixtures.ts", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", @@ -49,6 +50,7 @@ "svg-captcha": "^1.4.0" }, "devDependencies": { + "@compodoc/compodoc": "^2.0.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.18.0", "@nestjs/cli": "^11.0.0", diff --git a/scripts/seed-insurer-reports-fixtures.ts b/scripts/seed-insurer-reports-fixtures.ts new file mode 100644 index 0000000..7659bef --- /dev/null +++ b/scripts/seed-insurer-reports-fixtures.ts @@ -0,0 +1,2427 @@ +/** + * Seed a large, repeatable insurer-visible portfolio for report endpoint testing. + * + * What it creates + * - 1000 shared publicIds by default (configurable) + * - mixed V2/V3/V4/V5/V6-style blame/claim histories + * - expertFileActivities for blame experts, damage experts, and field experts + * - insurer-visible claim/blame status variety for unified-status reports + * - ratings/user ratings so top-files/top-experts endpoints have meaningful data + * + * Safety / repeatability + * - only deletes documents previously created by this script for the same seed tag + * - seed actors/branches are deterministic and reused across reruns + * + * Usage + * npm run seed:reports-fixtures + * + * Optional env + * SEED_REPORTS_CLIENT_KEY= + * SEED_REPORTS_CLIENT_CODE=8 + * SEED_REPORTS_COUNT=1000 + * SEED_REPORTS_TAG=reports-load-v1 + * SEED_REPORTS_PUBLIC_PREFIX=RPT1 + * SEED_REPORTS_DEFAULT_PASSWORD=Reports@724 + */ +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import * as crypto from "node:crypto"; +import mongoose, { Types } from "mongoose"; + +type AnyDoc = Record; +type ObjectId = Types.ObjectId; + +type FlowKind = + | "V2_USER" + | "V2_EXPERT_INIT" + | "V3" + | "V4" + | "V5" + | "V6"; + +type UnifiedBucket = + | "IN_PROGRESS" + | "WAITING_FOR_SECOND_PARTY" + | "WAITING_FOR_BLAME_EXPERT" + | "WAITING_FOR_SIGNATURES" + | "WAITING_FOR_DOCUMENT_RESEND" + | "WAITING_FOR_DAMAGE_EXPERT" + | "EXPERT_REVIEWING" + | "EXPERT_VALIDATING_FACTORS" + | "INSURER_REVIEW" + | "COMPLETED" + | "CANCELLED" + | "REJECTED" + | "STOPPED"; + +type Scenario = { + bucket: UnifiedBucket; + flow: FlowKind; + fileType: "THIRD_PARTY" | "CAR_BODY"; + blameStatus: string; + blameKind: "expert" | "field_expert" | "file_maker" | "call_center" | null; + blameActivity: "handled" | "checked" | "unchecked" | null; + hasClaim: boolean; + claimStatus?: string; + claimReviewStatus?: string; + claimStage: + | "none" + | "user_progress" + | "queue" + | "reviewing" + | "resend" + | "factor_validation" + | "insurer_review" + | "file_maker_wait" + | "file_maker_rejected" + | "completed" + | "rejected" + | "cancelled"; + claimActorKind: "damage_expert" | "field_expert" | null; + claimActivity: "handled" | "checked" | "unchecked" | null; + rateBlame: boolean; + rateClaim: boolean; + userRated: boolean; + objection: boolean; +}; + +type SeedActor = { + _id: ObjectId; + firstName: string; + lastName: string; + fullName: string; + email?: string; + branchId?: ObjectId; + branchCode?: string; + role: string; +}; + +type BranchRef = { + _id: ObjectId; + code: string; + name: string; + city: string; + state: string; + address: string; +}; + +type SeedContext = { + client: AnyDoc; + clientId: ObjectId; + clientIdStr: string; + count: number; + seedTag: string; + publicPrefix: string; + defaultPassword: string; + rng: () => number; + branches: BranchRef[]; + experts: SeedActor[]; + damageExperts: SeedActor[]; + fieldExperts: SeedActor[]; + fileMakers: SeedActor[]; + fileReviewers: SeedActor[]; + callCenters: SeedActor[]; + externalClientIds: ObjectId[]; + externalNames: string[]; +}; + +const SCRIPT_NAME = "seed-insurer-reports-fixtures"; + +const ROLE = { + EXPERT: "expert", + DAMAGE_EXPERT: "damage_expert", + FIELD_EXPERT: "field_expert", + FILE_MAKER: "file_maker", + FILE_REVIEWER: "file_reviewer", + CALL_CENTER: "call_center", +} as const; + +const USER_TYPE = { + LEGAL: "legal", +} as const; + +const CASE_STATUS = { + OPEN: "OPEN", + WAITING_FOR_SECOND_PARTY: "WAITING_FOR_SECOND_PARTY", + WAITING_FOR_EXPERT: "WAITING_FOR_EXPERT", + WAITING_FOR_DOCUMENT_RESEND: "WAITING_FOR_DOCUMENT_RESEND", + WAITING_FOR_SIGNATURES: "WAITING_FOR_SIGNATURES", + WAITING_FOR_FILE_REVIEWER: "WAITING_FOR_FILE_REVIEWER", + WAITING_FOR_FILE_MAKER_APPROVAL: "WAITING_FOR_FILE_MAKER_APPROVAL", + FILE_MAKER_REJECTED: "FILE_MAKER_REJECTED", + COMPLETED: "COMPLETED", + CANCELLED: "CANCELLED", + AUTO_CLOSED: "AUTO_CLOSED", + STOPPED: "STOPPED", +} as const; + +const CLAIM_CASE_STATUS = { + CREATED: "CREATED", + SELECTING_OUTER_PARTS: "SELECTING_OUTER_PARTS", + SELECTING_OTHER_PARTS: "SELECTING_OTHER_PARTS", + UPLOADING_REQUIRED_DOCUMENTS: "UPLOADING_REQUIRED_DOCUMENTS", + CAPTURING_PART_DAMAGES: "CAPTURING_PART_DAMAGES", + WAITING_FOR_USER_RESEND: "WAITING_FOR_USER_RESEND", + WAITING_FOR_DAMAGE_EXPERT: "WAITING_FOR_DAMAGE_EXPERT", + EXPERT_REVIEWING: "EXPERT_REVIEWING", + WAITING_FOR_INSURER_APPROVAL: "WAITING_FOR_INSURER_APPROVAL", + INSURER_REVIEW_AWAITING_OWNER_SIGN: "INSURER_REVIEW_AWAITING_OWNER_SIGN", + INSURER_REVIEW_MIXED_FACTORS_PENDING: "INSURER_REVIEW_MIXED_FACTORS_PENDING", + OWNER_REPAIR_FACTOR_UPLOAD_PENDING: "OWNER_REPAIR_FACTOR_UPLOAD_PENDING", + EXPERT_VALIDATING_REPAIR_FACTORS: "EXPERT_VALIDATING_REPAIR_FACTORS", + WAITING_FOR_FILE_REVIEWER: "WAITING_FOR_FILE_REVIEWER", + WAITING_FOR_FILE_MAKER_APPROVAL: "WAITING_FOR_FILE_MAKER_APPROVAL", + FILE_MAKER_REJECTED: "FILE_MAKER_REJECTED", + COMPLETED: "COMPLETED", + CANCELLED: "CANCELLED", + REJECTED: "REJECTED", +} as const; + +const CLAIM_STATUS = { + PENDING: "PENDING", + UNDER_REVIEW: "UNDER_REVIEW", + APPROVED: "APPROVED", + REJECTED: "REJECTED", + NEEDS_REVISION: "NEEDS_REVISION", +} as const; + +const CLAIM_STEP = { + CLAIM_CREATED: "CLAIM_CREATED", + SELECT_OUTER_PARTS: "SELECT_OUTER_PARTS", + SELECT_OTHER_PARTS: "SELECT_OTHER_PARTS", + CAPTURE_PART_DAMAGES: "CAPTURE_PART_DAMAGES", + UPLOAD_REQUIRED_DOCUMENTS: "UPLOAD_REQUIRED_DOCUMENTS", + USER_SUBMISSION_COMPLETE: "USER_SUBMISSION_COMPLETE", + USER_EXPERT_RESEND: "USER_EXPERT_RESEND", + EXPERT_DAMAGE_ASSESSMENT: "EXPERT_DAMAGE_ASSESSMENT", + EXPERT_FINAL_REPLY: "EXPERT_FINAL_REPLY", + EXPERT_COST_EVALUATION: "EXPERT_COST_EVALUATION", + OWNER_UPLOAD_FACTOR_DOCUMENTS: "OWNER_UPLOAD_FACTOR_DOCUMENTS", + INSURER_REVIEW: "INSURER_REVIEW", + CLAIM_COMPLETED: "CLAIM_COMPLETED", +} as const; + +const ACTIVITY = { + CHECKED: "checked", + HANDLED: "handled", + UNCHECKED: "unchecked", +} as const; + +const FILE_KIND = { + BLAME: "blame", + CLAIM: "claim", +} as const; + +const CREATION_METHOD = { + LINK: "LINK", + IN_PERSON: "IN_PERSON", +} as const; + +const FILLED_BY = { + CUSTOMER: "CUSTOMER", + EXPERT: "EXPERT", +} as const; + +const BUCKET_WEIGHTS: Record = { + IN_PROGRESS: 0.18, + WAITING_FOR_SECOND_PARTY: 0.07, + WAITING_FOR_BLAME_EXPERT: 0.08, + WAITING_FOR_SIGNATURES: 0.05, + WAITING_FOR_DOCUMENT_RESEND: 0.09, + WAITING_FOR_DAMAGE_EXPERT: 0.11, + EXPERT_REVIEWING: 0.09, + EXPERT_VALIDATING_FACTORS: 0.055, + INSURER_REVIEW: 0.12, + COMPLETED: 0.11, + CANCELLED: 0.02, + REJECTED: 0.015, + STOPPED: 0.01, +}; + +const FLOWS_BY_BUCKET: Record = { + IN_PROGRESS: ["V2_USER", "V2_EXPERT_INIT", "V4", "V5", "V6"], + WAITING_FOR_SECOND_PARTY: ["V2_USER", "V6"], + WAITING_FOR_BLAME_EXPERT: ["V2_USER", "V3", "V6"], + WAITING_FOR_SIGNATURES: ["V2_USER", "V2_EXPERT_INIT"], + WAITING_FOR_DOCUMENT_RESEND: ["V2_USER", "V2_EXPERT_INIT", "V3", "V6"], + WAITING_FOR_DAMAGE_EXPERT: [ + "V2_USER", + "V2_EXPERT_INIT", + "V3", + "V4", + "V5", + "V6", + ], + EXPERT_REVIEWING: [ + "V2_USER", + "V2_EXPERT_INIT", + "V3", + "V4", + "V5", + "V6", + ], + EXPERT_VALIDATING_FACTORS: [ + "V2_USER", + "V2_EXPERT_INIT", + "V3", + "V4", + "V5", + "V6", + ], + INSURER_REVIEW: [ + "V2_USER", + "V2_EXPERT_INIT", + "V3", + "V4", + "V5", + "V6", + ], + COMPLETED: ["V2_USER", "V2_EXPERT_INIT", "V3", "V4", "V5", "V6"], + CANCELLED: ["V2_USER", "V2_EXPERT_INIT", "V4", "V5", "V6"], + REJECTED: ["V2_USER", "V2_EXPERT_INIT", "V3", "V4", "V5", "V6"], + STOPPED: ["V2_USER", "V6"], +}; + +function stripQuotes(value: string): string { + const trimmed = value.trim(); + if ( + (trimmed.startsWith("'") && trimmed.endsWith("'")) || + (trimmed.startsWith('"') && trimmed.endsWith('"')) + ) { + return trimmed.slice(1, -1); + } + return trimmed; +} + +function stripInlineComment(value: string): string { + const hashIdx = value.indexOf(" #"); + return hashIdx === -1 ? value : value.slice(0, hashIdx).trim(); +} + +function expandEnvValue(value: string, env: NodeJS.ProcessEnv): string { + return value.replace(/\$\{([^}]+)\}/g, (_, key: string) => env[key] ?? ""); +} + +function loadEnvFile() { + const envPath = join(process.cwd(), ".env"); + if (!existsSync(envPath)) return; + + const raw: Record = {}; + for (const line of readFileSync(envPath, "utf8").split("\n")) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const idx = trimmed.indexOf("="); + if (idx === -1) continue; + const key = trimmed.slice(0, idx).trim(); + const value = stripInlineComment(trimmed.slice(idx + 1).trim()); + raw[key] = value; + } + + for (const [key, value] of Object.entries(raw)) { + if (process.env[key]) continue; + process.env[key] = stripQuotes(value); + } + + for (let pass = 0; pass < 5; pass++) { + let changed = false; + for (const key of Object.keys(process.env)) { + const current = process.env[key]; + if (!current || !current.includes("${")) continue; + const expanded = expandEnvValue(stripQuotes(current), process.env); + if (expanded !== current) { + process.env[key] = expanded; + changed = true; + } + } + if (!changed) break; + } + + for (const key of Object.keys(process.env)) { + const value = process.env[key]; + if (value) process.env[key] = stripQuotes(value); + } +} + +function resolveMongoUri(): string { + const uri = process.env.MONGO_URI?.trim(); + if (!uri) throw new Error("MONGO_URI is not set in .env"); + if (!uri.startsWith("mongodb://") && !uri.startsWith("mongodb+srv://")) { + throw new Error(`Invalid MONGO_URI after env expansion: ${uri.slice(0, 50)}...`); + } + return uri; +} + +function hashPassword(password: string): Promise { + return new Promise((resolve, reject) => { + const salt = crypto.randomBytes(16).toString("hex"); + crypto.scrypt(password, salt, 64, (err, derivedKey) => { + if (err) reject(err); + else resolve(`${salt}:${derivedKey.toString("hex")}`); + }); + }); +} + +function asObjectId(value: string | ObjectId): ObjectId { + return value instanceof Types.ObjectId ? value : new Types.ObjectId(value); +} + +function seedToInt(seed: string): number { + const hex = crypto.createHash("sha256").update(seed).digest("hex").slice(0, 8); + return parseInt(hex, 16) >>> 0; +} + +function mulberry32(seed: number): () => number { + let t = seed >>> 0; + return () => { + t += 0x6d2b79f5; + let x = Math.imul(t ^ (t >>> 15), t | 1); + x ^= x + Math.imul(x ^ (x >>> 7), x | 61); + return ((x ^ (x >>> 14)) >>> 0) / 4294967296; + }; +} + +function randInt(rng: () => number, min: number, max: number) { + return Math.floor(rng() * (max - min + 1)) + min; +} + +function pick(items: T[], rng: () => number): T { + return items[Math.floor(rng() * items.length)]; +} + +function pad(n: number, len = 5): string { + return String(n).padStart(len, "0"); +} + +function shortHash(input: string, len = 4): string { + return crypto.createHash("sha1").update(input).digest("hex").slice(0, len).toUpperCase(); +} + +function addMinutes(date: Date, minutes: number): Date { + return new Date(date.getTime() + minutes * 60_000); +} + +function addHours(date: Date, hours: number): Date { + return addMinutes(date, hours * 60); +} + +function clone(value: T): T { + return JSON.parse(JSON.stringify(value)); +} + +function maybe(condition: boolean, value: T): T | undefined { + return condition ? value : undefined; +} + +async function resolveClient(db: mongoose.mongo.Db): Promise { + const clients = db.collection("clients"); + const clientKey = process.env.SEED_REPORTS_CLIENT_KEY?.trim(); + const clientCodeRaw = process.env.SEED_REPORTS_CLIENT_CODE?.trim() || "8"; + const clientCode = Number(clientCodeRaw); + + if (clientKey) { + if (!Types.ObjectId.isValid(clientKey)) { + throw new Error("SEED_REPORTS_CLIENT_KEY must be a valid Mongo ObjectId"); + } + const client = await clients.findOne({ _id: new Types.ObjectId(clientKey) }); + if (!client) throw new Error(`Client not found for key ${clientKey}`); + return client; + } + + if (!Number.isFinite(clientCode)) { + throw new Error("SEED_REPORTS_CLIENT_CODE must be numeric when provided"); + } + + const client = await clients.findOne({ clientCode }); + if (!client) { + throw new Error( + `Client not found for clientCode=${clientCode}. Set SEED_REPORTS_CLIENT_KEY explicitly.`, + ); + } + return client; +} + +async function ensureBranches( + db: mongoose.mongo.Db, + clientId: ObjectId, + clientName: string, +): Promise { + const branches = db.collection("branches"); + const desired = [ + { code: "RPT-NORTH", name: `${clientName} شمال`, city: "تهران", state: "تهران" }, + { code: "RPT-CENTER", name: `${clientName} مرکز`, city: "تهران", state: "تهران" }, + { code: "RPT-SOUTH", name: `${clientName} جنوب`, city: "تهران", state: "تهران" }, + ]; + + for (let i = 0; i < desired.length; i++) { + const seed = desired[i]; + await branches.updateOne( + { clientKey: clientId, code: seed.code }, + { + $set: { + clientKey: clientId, + code: seed.code, + name: seed.name, + city: seed.city, + state: seed.state, + address: `${seed.city} - ${seed.name} - شعبه دیتای تست گزارش`, + phoneNumber: `02188${pad(i + 1, 4)}`, + isActive: true, + activityStartDate: new Date(), + updatedAt: new Date(), + seedMeta: { script: SCRIPT_NAME, kind: "branch" }, + }, + $setOnInsert: { createdAt: new Date() }, + }, + { upsert: true }, + ); + } + + const out = await branches + .find({ clientKey: clientId, code: { $in: desired.map((d) => d.code) } }) + .sort({ code: 1 }) + .toArray(); + + return out.map((b) => ({ + _id: asObjectId(b._id), + code: String(b.code), + name: String(b.name), + city: String(b.city), + state: String(b.state), + address: String(b.address), + })); +} + +async function ensureRoster(ctx: { + db: mongoose.mongo.Db; + clientId: ObjectId; + branches: BranchRef[]; + passwordHash: string; + publicPrefix: string; +}): Promise<{ + experts: SeedActor[]; + damageExperts: SeedActor[]; + fieldExperts: SeedActor[]; + fileMakers: SeedActor[]; + fileReviewers: SeedActor[]; + callCenters: SeedActor[]; +}> { + const { db, clientId, branches, passwordHash, publicPrefix } = ctx; + + const ensureActors = async (args: { + collectionName: string; + role: string; + count: number; + branchScoped: boolean; + clientKeyMode: "string" | "objectId"; + prefix: string; + }): Promise => { + const col = db.collection(args.collectionName); + const out: SeedActor[] = []; + + for (let i = 0; i < args.count; i++) { + const index = i + 1; + const branch = branches[i % branches.length]; + const firstName = `${args.prefix}`; + const lastName = `Seed ${pad(index, 2)}`; + const email = `${publicPrefix.toLowerCase()}.${args.prefix.toLowerCase()}.${pad(index, 2)}@seed.local`; + const nationalCode = `${randNationalBase(args.prefix)}${pad(index, 4)}`.slice(0, 10); + const payload: AnyDoc = { + firstName, + lastName, + email, + username: email, + nationalCode, + password: passwordHash, + mobile: `0912${pad(index + args.collectionName.length * 13, 6)}`, + phone: `02177${pad(index, 4)}`, + role: args.role, + otp: "", + state: "تهران", + city: "تهران", + address: `آدرس تست ${args.prefix} ${index}`, + updatedAt: new Date(), + seedMeta: { script: SCRIPT_NAME, kind: "actor", role: args.role }, + }; + + if (args.branchScoped) { + payload.branchId = branch._id; + payload.branchCode = branch.code; + } + if (args.collectionName === "expert") { + payload.userType = USER_TYPE.LEGAL; + payload.clientKey = String(clientId); + payload.sheba = `IR${pad(100000000000000000 + index, 24)}`; + payload.insuActivityCo = String(clientId); + } else if (args.collectionName === "damage-expert") { + payload.userType = USER_TYPE.LEGAL; + payload.clientKey = clientId; + payload.insuActivityCo = String(clientId); + } else if (args.collectionName === "file-maker") { + payload.clientKey = clientId; + payload.locations = [{ id: branch.code, name: branch.name }]; + } else if (args.collectionName === "file-reviewer") { + payload.clientKey = clientId; + payload.locations = [{ id: branch.code, name: branch.name }]; + } else if (args.collectionName === "field-expert") { + payload.clientKey = clientId; + } else if (args.collectionName === "call-center-agents") { + payload.clientKey = clientId; + } + + await col.updateOne( + { email }, + { $set: payload, $setOnInsert: { createdAt: new Date() } }, + { upsert: true }, + ); + + const doc = await col.findOne({ email }); + if (!doc) throw new Error(`Failed to load upserted actor ${email}`); + out.push({ + _id: asObjectId(doc._id), + firstName: String(doc.firstName), + lastName: String(doc.lastName), + fullName: `${doc.firstName} ${doc.lastName}`.trim(), + email, + branchId: doc.branchId ? asObjectId(doc.branchId) : undefined, + branchCode: doc.branchCode ? String(doc.branchCode) : undefined, + role: args.role, + }); + } + + return out; + }; + + return { + experts: await ensureActors({ + collectionName: "expert", + role: ROLE.EXPERT, + count: 6, + branchScoped: true, + clientKeyMode: "string", + prefix: "BlameExpert", + }), + damageExperts: await ensureActors({ + collectionName: "damage-expert", + role: ROLE.DAMAGE_EXPERT, + count: 8, + branchScoped: true, + clientKeyMode: "objectId", + prefix: "DamageExpert", + }), + fieldExperts: await ensureActors({ + collectionName: "field-expert", + role: ROLE.FIELD_EXPERT, + count: 4, + branchScoped: true, + clientKeyMode: "objectId", + prefix: "FieldExpert", + }), + fileMakers: await ensureActors({ + collectionName: "file-maker", + role: ROLE.FILE_MAKER, + count: 3, + branchScoped: true, + clientKeyMode: "objectId", + prefix: "FileMaker", + }), + fileReviewers: await ensureActors({ + collectionName: "file-reviewer", + role: ROLE.FILE_REVIEWER, + count: 3, + branchScoped: true, + clientKeyMode: "objectId", + prefix: "FileReviewer", + }), + callCenters: await ensureActors({ + collectionName: "call-center-agents", + role: ROLE.CALL_CENTER, + count: 2, + branchScoped: false, + clientKeyMode: "objectId", + prefix: "CallCenter", + }), + }; +} + +function randNationalBase(prefix: string): string { + const n = prefix + .split("") + .reduce((acc, ch) => acc + ch.charCodeAt(0), 0) + .toString() + .slice(0, 6); + return n.padEnd(6, "7"); +} + +async function purgeSeedPortfolio(db: mongoose.mongo.Db, seedTag: string) { + const blameCases = db.collection("blameCases"); + const claimCases = db.collection("claimCases"); + const expertFileActivities = db.collection("expertFileActivities"); + + const [existingBlames, existingClaims] = await Promise.all([ + blameCases.find({ "seedMeta.script": SCRIPT_NAME, "seedMeta.tag": seedTag }, { projection: { _id: 1 } }).toArray(), + claimCases.find({ "seedMeta.script": SCRIPT_NAME, "seedMeta.tag": seedTag }, { projection: { _id: 1 } }).toArray(), + ]); + + const fileIds = [...existingBlames, ...existingClaims].map((d) => asObjectId(d._id)); + + const [deletedBlames, deletedClaims, deletedActivities] = await Promise.all([ + blameCases.deleteMany({ "seedMeta.script": SCRIPT_NAME, "seedMeta.tag": seedTag }), + claimCases.deleteMany({ "seedMeta.script": SCRIPT_NAME, "seedMeta.tag": seedTag }), + expertFileActivities.deleteMany({ + $or: [ + { "seedMeta.script": SCRIPT_NAME, "seedMeta.tag": seedTag }, + ...(fileIds.length ? [{ fileId: { $in: fileIds } }] : []), + ], + }), + ]); + + return { + deletedBlames: deletedBlames.deletedCount ?? 0, + deletedClaims: deletedClaims.deletedCount ?? 0, + deletedActivities: deletedActivities.deletedCount ?? 0, + }; +} + +function scaleBucketCounts(total: number): Record { + const keys = Object.keys(BUCKET_WEIGHTS) as UnifiedBucket[]; + const raw = keys.map((key) => ({ key, exact: BUCKET_WEIGHTS[key] * total })); + const counts = Object.fromEntries(raw.map(({ key, exact }) => [key, Math.floor(exact)])) as Record< + UnifiedBucket, + number + >; + let assigned = keys.reduce((sum, key) => sum + counts[key], 0); + const byRemainder = raw + .map(({ key, exact }) => ({ key, remainder: exact - Math.floor(exact) })) + .sort((a, b) => b.remainder - a.remainder); + let i = 0; + while (assigned < total) { + counts[byRemainder[i % byRemainder.length].key] += 1; + assigned += 1; + i += 1; + } + return counts; +} + +function shuffleInPlace(items: T[], rng: () => number) { + for (let i = items.length - 1; i > 0; i--) { + const j = Math.floor(rng() * (i + 1)); + [items[i], items[j]] = [items[j], items[i]]; + } +} + +function buildBucketQueue(count: number, rng: () => number): UnifiedBucket[] { + const counts = scaleBucketCounts(count); + const queue: UnifiedBucket[] = []; + for (const [bucket, qty] of Object.entries(counts) as Array<[UnifiedBucket, number]>) { + for (let i = 0; i < qty; i++) queue.push(bucket); + } + shuffleInPlace(queue, rng); + return queue; +} + +function selectFlow(bucket: UnifiedBucket, indexInBucket: number): FlowKind { + const flows = FLOWS_BY_BUCKET[bucket]; + return flows[indexInBucket % flows.length]; +} + +function selectFileType(bucket: UnifiedBucket, flow: FlowKind, ordinal: number): "THIRD_PARTY" | "CAR_BODY" { + if (bucket === "WAITING_FOR_SECOND_PARTY" || bucket === "STOPPED") return "THIRD_PARTY"; + if (flow === "V6") return "THIRD_PARTY"; + return ordinal % 5 === 0 ? "CAR_BODY" : "THIRD_PARTY"; +} + +function makeScenario(bucket: UnifiedBucket, flow: FlowKind, fileType: "THIRD_PARTY" | "CAR_BODY", ordinal: number): Scenario { + switch (bucket) { + case "WAITING_FOR_SECOND_PARTY": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.WAITING_FOR_SECOND_PARTY, + blameKind: flow === "V6" ? "call_center" : null, + blameActivity: null, + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: false, + rateClaim: false, + userRated: false, + objection: false, + }; + case "WAITING_FOR_BLAME_EXPERT": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.WAITING_FOR_EXPERT, + blameKind: flow === "V3" ? "field_expert" : "expert", + blameActivity: flow === "V3" ? "handled" : "checked", + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: flow !== "V3", + rateClaim: false, + userRated: false, + objection: false, + }; + case "WAITING_FOR_SIGNATURES": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.WAITING_FOR_SIGNATURES, + blameKind: flow === "V2_EXPERT_INIT" ? "field_expert" : "expert", + blameActivity: "handled", + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: true, + rateClaim: false, + userRated: false, + objection: false, + }; + case "WAITING_FOR_DOCUMENT_RESEND": { + const claimSide = ordinal % 2 === 0; + return claimSide + ? { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : "expert", + blameActivity: "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.WAITING_FOR_USER_RESEND, + claimReviewStatus: CLAIM_STATUS.NEEDS_REVISION, + claimStage: "resend", + claimActorKind: "damage_expert", + claimActivity: "handled", + rateBlame: true, + rateClaim: false, + userRated: false, + objection: true, + } + : { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.WAITING_FOR_DOCUMENT_RESEND, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : "expert", + blameActivity: "handled", + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: true, + rateClaim: false, + userRated: false, + objection: false, + }; + } + case "WAITING_FOR_DAMAGE_EXPERT": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.WAITING_FOR_DAMAGE_EXPERT, + claimReviewStatus: CLAIM_STATUS.PENDING, + claimStage: "queue", + claimActorKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : null, + claimActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : null, + rateBlame: flow !== "V4" && flow !== "V5", + rateClaim: false, + userRated: false, + objection: false, + }; + case "EXPERT_REVIEWING": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.EXPERT_REVIEWING, + claimReviewStatus: CLAIM_STATUS.UNDER_REVIEW, + claimStage: "reviewing", + claimActorKind: "damage_expert", + claimActivity: "checked", + rateBlame: flow !== "V4" && flow !== "V5", + rateClaim: false, + userRated: false, + objection: false, + }; + case "EXPERT_VALIDATING_FACTORS": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.EXPERT_VALIDATING_REPAIR_FACTORS, + claimReviewStatus: CLAIM_STATUS.UNDER_REVIEW, + claimStage: "factor_validation", + claimActorKind: "damage_expert", + claimActivity: "checked", + rateBlame: flow !== "V4" && flow !== "V5", + rateClaim: true, + userRated: false, + objection: ordinal % 3 === 0, + }; + case "INSURER_REVIEW": { + const statuses = [ + CLAIM_CASE_STATUS.WAITING_FOR_INSURER_APPROVAL, + CLAIM_CASE_STATUS.INSURER_REVIEW_AWAITING_OWNER_SIGN, + CLAIM_CASE_STATUS.INSURER_REVIEW_MIXED_FACTORS_PENDING, + CLAIM_CASE_STATUS.OWNER_REPAIR_FACTOR_UPLOAD_PENDING, + ]; + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: statuses[ordinal % statuses.length], + claimReviewStatus: CLAIM_STATUS.APPROVED, + claimStage: "insurer_review", + claimActorKind: "damage_expert", + claimActivity: "handled", + rateBlame: flow !== "V4" && flow !== "V5", + rateClaim: true, + userRated: ordinal % 2 === 0, + objection: ordinal % 5 === 0, + }; + } + case "COMPLETED": { + const blameOnly = ordinal % 5 === 0 && (flow === "V2_USER" || flow === "V6"); + return blameOnly + ? { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: "expert", + blameActivity: "handled", + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: true, + rateClaim: false, + userRated: false, + objection: false, + } + : { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.COMPLETED, + claimReviewStatus: CLAIM_STATUS.APPROVED, + claimStage: "completed", + claimActorKind: "damage_expert", + claimActivity: "handled", + rateBlame: flow !== "V4" && flow !== "V5", + rateClaim: true, + userRated: true, + objection: ordinal % 8 === 0, + }; + } + case "CANCELLED": { + const claimSide = ordinal % 3 !== 0; + return claimSide + ? { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.CANCELLED, + claimReviewStatus: CLAIM_STATUS.NEEDS_REVISION, + claimStage: "cancelled", + claimActorKind: ordinal % 2 === 0 ? "damage_expert" : null, + claimActivity: ordinal % 2 === 0 ? "unchecked" : null, + rateBlame: false, + rateClaim: false, + userRated: false, + objection: false, + } + : { + bucket, + flow, + fileType, + blameStatus: ordinal % 2 === 0 ? CASE_STATUS.CANCELLED : CASE_STATUS.AUTO_CLOSED, + blameKind: ordinal % 2 === 0 ? "expert" : null, + blameActivity: ordinal % 2 === 0 ? "unchecked" : null, + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: false, + rateClaim: false, + userRated: false, + objection: false, + }; + } + case "REJECTED": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" || flow === "V3" ? "field_expert" : flow === "V4" || flow === "V5" ? "file_maker" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" || flow === "V3" ? "handled" : flow === "V4" || flow === "V5" ? null : "handled", + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.REJECTED, + claimReviewStatus: CLAIM_STATUS.REJECTED, + claimStage: "rejected", + claimActorKind: "damage_expert", + claimActivity: "handled", + rateBlame: false, + rateClaim: true, + userRated: ordinal % 3 === 0, + objection: true, + }; + case "STOPPED": + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.STOPPED, + blameKind: ordinal % 2 === 0 ? "expert" : null, + blameActivity: ordinal % 2 === 0 ? "unchecked" : null, + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: false, + rateClaim: false, + userRated: false, + objection: false, + }; + case "IN_PROGRESS": { + if (flow === "V5") { + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: "file_maker", + blameActivity: null, + hasClaim: true, + claimStatus: + ordinal % 2 === 0 + ? CLAIM_CASE_STATUS.WAITING_FOR_FILE_MAKER_APPROVAL + : CLAIM_CASE_STATUS.FILE_MAKER_REJECTED, + claimReviewStatus: CLAIM_STATUS.APPROVED, + claimStage: + ordinal % 2 === 0 ? "file_maker_wait" : "file_maker_rejected", + claimActorKind: "damage_expert", + claimActivity: "handled", + rateBlame: false, + rateClaim: true, + userRated: false, + objection: ordinal % 4 === 0, + }; + } + if (flow === "V4") { + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: "file_maker", + blameActivity: null, + hasClaim: true, + claimStatus: CLAIM_CASE_STATUS.WAITING_FOR_FILE_REVIEWER, + claimReviewStatus: CLAIM_STATUS.PENDING, + claimStage: "user_progress", + claimActorKind: null, + claimActivity: null, + rateBlame: false, + rateClaim: false, + userRated: false, + objection: false, + }; + } + if (flow === "V2_USER" && ordinal % 3 === 0) { + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.OPEN, + blameKind: null, + blameActivity: null, + hasClaim: false, + claimStage: "none", + claimActorKind: null, + claimActivity: null, + rateBlame: false, + rateClaim: false, + userRated: false, + objection: false, + }; + } + const progressStatuses = [ + CLAIM_CASE_STATUS.CREATED, + CLAIM_CASE_STATUS.SELECTING_OUTER_PARTS, + CLAIM_CASE_STATUS.SELECTING_OTHER_PARTS, + CLAIM_CASE_STATUS.CAPTURING_PART_DAMAGES, + CLAIM_CASE_STATUS.UPLOADING_REQUIRED_DOCUMENTS, + ]; + const claimActorKind = flow === "V2_EXPERT_INIT" ? "field_expert" : null; + return { + bucket, + flow, + fileType, + blameStatus: CASE_STATUS.COMPLETED, + blameKind: flow === "V2_EXPERT_INIT" ? "field_expert" : flow === "V6" ? "call_center" : "expert", + blameActivity: flow === "V2_EXPERT_INIT" ? "handled" : flow === "V6" ? null : ordinal % 4 === 0 ? "handled" : null, + hasClaim: true, + claimStatus: progressStatuses[ordinal % progressStatuses.length], + claimReviewStatus: CLAIM_STATUS.PENDING, + claimStage: "user_progress", + claimActorKind, + claimActivity: claimActorKind ? "handled" : null, + rateBlame: flow === "V2_EXPERT_INIT", + rateClaim: false, + userRated: false, + objection: false, + }; + } + } +} + +function actorRef(actor: SeedActor | undefined, actorType: string): AnyDoc | undefined { + if (!actor) return undefined; + return { + actorId: actor._id, + actorName: actor.fullName, + actorType, + }; +} + +function expertProfileSnapshot(actor: SeedActor | undefined): AnyDoc | undefined { + if (!actor) return undefined; + return { + fullName: actor.fullName, + firstName: actor.firstName, + lastName: actor.lastName, + email: actor.email, + }; +} + +function buildCreatedAt(index: number, rng: () => number): Date { + const monthOffset = index % 5; + const monthStart = new Date(); + monthStart.setHours(8, 0, 0, 0); + monthStart.setDate(1); + monthStart.setMonth(monthStart.getMonth() - monthOffset); + const day = randInt(rng, 1, 24); + const hour = randInt(rng, 8, 18); + const minute = randInt(rng, 0, 50); + return new Date( + monthStart.getFullYear(), + monthStart.getMonth(), + day, + hour, + minute, + randInt(rng, 0, 59), + ); +} + +function makePlate(index: number): string { + return `${10 + (index % 89)}الف${100 + (index % 900)}-${10 + (index % 89)}`; +} + +function makeInsurerRating(index: number): AnyDoc { + const base = 2.8 + ((index % 18) / 10); + return { + collisionMethodAccuracy: Number(Math.min(5, base + 0.3).toFixed(1)), + evaluationTimeliness: Number(Math.min(5, base - 0.1).toFixed(1)), + accidentCauseAccuracy: Number(Math.min(5, base + 0.2).toFixed(1)), + guiltyVehicleIdentification: Number(Math.min(5, base).toFixed(1)), + botRating: Number(Math.min(5, base + 0.4).toFixed(1)), + }; +} + +function makeUserRating(index: number): AnyDoc { + return { + progressSpeed: 3 + (index % 3), + registrationEase: 2 + (index % 4), + overallEvaluation: 3 + (index % 3), + comment: `Synthetic satisfaction sample #${index + 1}`, + }; +} + +function makeDamageParts(index: number): AnyDoc[] { + return [ + { + id: `part-${index % 7}-front-bumper`, + name: "Front bumper", + side: "CENTER", + label_fa: "سپر جلو", + catalogKey: `front-bumper-${index % 7}`, + }, + { + id: `part-${index % 11}-hood`, + name: "Hood", + side: "CENTER", + label_fa: "کاپوت", + catalogKey: `hood-${index % 11}`, + }, + ]; +} + +function makeRequiredDocuments(index: number, stage: Scenario["claimStage"]): Record { + const base = { + nationalCard: { uploaded: true, fileId: new Types.ObjectId(), fileName: `national-card-${index}.jpg` }, + greenCard: { uploaded: true, fileId: new Types.ObjectId(), fileName: `green-card-${index}.jpg` }, + drivingLicense: { uploaded: stage !== "user_progress", fileId: new Types.ObjectId(), fileName: `license-${index}.jpg` }, + }; + + if (stage === "user_progress") { + return { + ...base, + damagePhotos: { uploaded: index % 2 === 0, fileId: new Types.ObjectId(), fileName: `damage-${index}.jpg` }, + }; + } + + if (stage === "resend") { + return { + ...base, + expertRequestedDoc: { uploaded: false, fileId: new Types.ObjectId(), fileName: `resend-${index}.jpg` }, + }; + } + + return base; +} + +function pickActorsForCase(ctx: SeedContext, index: number) { + return { + expert: ctx.experts[index % ctx.experts.length], + damageExpert: ctx.damageExperts[(index * 3) % ctx.damageExperts.length], + fieldExpert: ctx.fieldExperts[(index * 5) % ctx.fieldExperts.length], + fileMaker: ctx.fileMakers[index % ctx.fileMakers.length], + fileReviewer: ctx.fileReviewers[(index * 2) % ctx.fileReviewers.length], + callCenter: ctx.callCenters[index % ctx.callCenters.length], + }; +} + +function buildParties(ctx: SeedContext, scenario: Scenario, index: number) { + const targetName = `کاربر مقصر ${index + 1}`; + const damagedName = `زیان‌دیده ${index + 1}`; + const firstUserId = new Types.ObjectId(); + const secondUserId = new Types.ObjectId(); + const externalClientId = ctx.externalClientIds[index % ctx.externalClientIds.length]; + const externalName = ctx.externalNames[index % ctx.externalNames.length]; + + const firstParty: AnyDoc = { + role: "FIRST", + person: { + userId: firstUserId, + fullName: targetName, + phoneNumber: `091100${pad(index, 4)}`, + clientId: ctx.clientId, + birthday: "1370/01/01", + nationalCodeOfInsurer: `00${pad(index + 1000, 8)}`, + nationalCodeOfDriver: `00${pad(index + 2000, 8)}`, + }, + vehicle: { + carName: "Peugeot 206", + carModel: `139${index % 10}`, + plate: makePlate(index), + plateId: makePlate(index), + name: "Peugeot 206", + model: `139${index % 10}`, + }, + insurance: { + policyNumber: `POL-${ctx.publicPrefix}-${pad(index + 1, 6)}`, + company: ctx.client?.clientName?.persian ?? ctx.client?.clientName?.english ?? "Client", + startDate: "1403/01/01", + endDate: "1404/01/01", + financialCeiling: "53000000", + coverages: ["THIRD_PARTY"], + carBodyInsurance: { + policyNumber: `BDN-${ctx.publicPrefix}-${pad(index + 1, 6)}`, + insurerCompany: ctx.client?.clientName?.persian ?? "Client", + startDate: "1403/01/01", + endDate: "1404/01/01", + coverages: ["بدنه"], + }, + }, + statement: { + admitsGuilt: true, + claimsDamage: scenario.fileType === "CAR_BODY", + acceptsExpertOpinion: scenario.bucket !== "WAITING_FOR_BLAME_EXPERT", + description: "اعتراف به تقصیر / دیتای تست", + accidentDate: new Date(), + accidentTime: "10:30", + weatherCondition: "CLEAR", + roadCondition: "DRY", + lightCondition: "DAY", + }, + carBodyFirstForm: + scenario.fileType === "CAR_BODY" ? { car: true, object: false } : undefined, + confirmation: { + partyRole: "FIRST", + accepted: scenario.blameStatus === CASE_STATUS.COMPLETED, + }, + }; + + const secondParty: AnyDoc = { + role: "SECOND", + person: { + userId: secondUserId, + fullName: damagedName, + phoneNumber: `091200${pad(index, 4)}`, + clientId: externalClientId, + clientName: externalName, + birthday: "1372/02/02", + nationalCodeOfInsurer: `00${pad(index + 3000, 8)}`, + nationalCodeOfDriver: `00${pad(index + 4000, 8)}`, + }, + vehicle: { + carName: "Dena", + carModel: `140${index % 4}`, + plate: makePlate(index + 700), + plateId: makePlate(index + 700), + name: "Dena", + model: `140${index % 4}`, + }, + insurance: { + policyNumber: `EXT-${ctx.publicPrefix}-${pad(index + 1, 6)}`, + company: externalName, + startDate: "1403/01/01", + endDate: "1404/01/01", + financialCeiling: "53000000", + coverages: ["THIRD_PARTY"], + }, + statement: { + admitsGuilt: false, + claimsDamage: true, + acceptsExpertOpinion: scenario.blameStatus === CASE_STATUS.COMPLETED, + description: "زیان‌دیده / دیتای تست", + }, + confirmation: { + partyRole: "SECOND", + accepted: scenario.blameStatus === CASE_STATUS.COMPLETED, + }, + }; + + return { + parties: + scenario.fileType === "CAR_BODY" ? [firstParty] : [firstParty, secondParty], + firstPartyUserId: firstUserId, + secondPartyUserId: secondUserId, + ownerUserId: scenario.fileType === "CAR_BODY" ? firstUserId : secondUserId, + ownerFullName: scenario.fileType === "CAR_BODY" ? targetName : damagedName, + }; +} + +function pushHistory( + history: AnyDoc[], + date: Date, + offsetMinutes: number, + type: string, + actor?: AnyDoc, + metadata?: AnyDoc, +) { + history.push({ + type, + ...(actor ? { actor } : {}), + timestamp: addMinutes(date, offsetMinutes), + ...(metadata ? { metadata } : {}), + }); +} + +function buildBlameHistory( + ctx: SeedContext, + scenario: Scenario, + actors: ReturnType, + createdAt: Date, + fileType: "THIRD_PARTY" | "CAR_BODY", +) { + const history: AnyDoc[] = []; + if (scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3") { + pushHistory( + history, + createdAt, + 0, + "FILE_CREATED_BY_FIELD_EXPERT", + actorRef(actors.fieldExpert, "field_expert"), + { flow: scenario.flow }, + ); + pushHistory( + history, + createdAt, + 20, + fileType === "CAR_BODY" + ? "EXPERT_COMPLETED_CAR_BODY_FORM_V2" + : "EXPERT_COMPLETED_THIRD_PARTY_FORM_V2", + actorRef(actors.fieldExpert, "field_expert"), + ); + if (scenario.flow === "V3") { + pushHistory( + history, + createdAt, + 35, + "V3_BLAME_ACCIDENT_VIDEO_UPLOADED", + actorRef(actors.fieldExpert, "field_expert"), + ); + } + } else if (scenario.flow === "V6") { + pushHistory( + history, + createdAt, + 0, + "LINK_SENT", + actorRef(actors.callCenter, "call_center"), + { via: "call-center" }, + ); + pushHistory(history, createdAt, 15, "PARTY_OTP_SENT", actorRef(actors.callCenter, "call_center")); + pushHistory(history, createdAt, 35, "PARTY_OTP_VERIFIED", { actorType: "user", actorName: "User" }); + } else if (scenario.flow === "V4" || scenario.flow === "V5") { + pushHistory(history, createdAt, 0, "LINK_SENT", actorRef(actors.fileMaker, "file_maker"), { + flow: scenario.flow, + }); + pushHistory(history, createdAt, 20, "PARTY_OTPS_VERIFIED", actorRef(actors.fileMaker, "file_maker")); + } else { + pushHistory(history, createdAt, 0, "LINK_SENT", { actorType: "system" }, { flow: scenario.flow }); + pushHistory(history, createdAt, 10, "PARTY_OTP_SENT", { actorType: "system" }); + pushHistory(history, createdAt, 30, "PARTY_OTPS_VERIFIED", { actorType: "user", actorName: "User" }); + } + + if (scenario.blameStatus === CASE_STATUS.WAITING_FOR_SECOND_PARTY) { + pushHistory(history, createdAt, 60, "SECOND_PARTY_INVITED", { actorType: "system" }); + return history; + } + + if (scenario.fileType === "THIRD_PARTY") { + pushHistory(history, createdAt, 75, "SECOND_PARTY_OTP_VERIFIED_ADVANCED", { + actorType: "user", + actorName: "User", + }); + } + + if (scenario.blameKind === "expert") { + pushHistory(history, createdAt, 120, "BLAME_ASSIGNED", actorRef(actors.expert, "expert"), { + note: "Synthetic seed assignment", + }); + } + + if (scenario.blameStatus === CASE_STATUS.WAITING_FOR_DOCUMENT_RESEND) { + pushHistory( + history, + createdAt, + 160, + "BLAME_DOCUMENT_RESEND_STARTED", + scenario.blameKind === "field_expert" + ? actorRef(actors.fieldExpert, "field_expert") + : scenario.blameKind === "expert" + ? actorRef(actors.expert, "expert") + : actorRef(actors.fileMaker, "file_maker"), + { requestedItems: ["video", "voice", "description"] }, + ); + } else if ( + scenario.blameStatus === CASE_STATUS.WAITING_FOR_SIGNATURES || + scenario.blameStatus === CASE_STATUS.COMPLETED || + scenario.hasClaim + ) { + pushHistory( + history, + createdAt, + 170, + scenario.flow === "V3" + ? "V3_ACCIDENT_FIELDS_SAVED" + : "ACCIDENT_FIELDS_SAVED_ADVANCED_TO_SIGNATURES", + scenario.blameKind === "field_expert" + ? actorRef(actors.fieldExpert, "field_expert") + : scenario.blameKind === "expert" + ? actorRef(actors.expert, "expert") + : scenario.blameKind === "file_maker" + ? actorRef(actors.fileMaker, "file_maker") + : actorRef(actors.callCenter, "call_center"), + ); + } + + if (scenario.blameStatus === CASE_STATUS.STOPPED) { + pushHistory(history, createdAt, 200, "PARTY_REJECTED_EXPERT_DECISION", { actorType: "user", actorName: "User" }); + } + + return history; +} + +function buildClaimWorkflow( + scenario: Scenario, + actors: ReturnType, + createdAt: Date, +) { + const assignedDamage = actorRef(actors.damageExpert, "damage_expert"); + const assignedField = actorRef(actors.fieldExpert, "field_expert"); + + if (!scenario.hasClaim) return undefined; + + const base: AnyDoc = { locked: false, completedSteps: [CLAIM_STEP.CLAIM_CREATED] }; + + switch (scenario.claimStage) { + case "user_progress": { + const statusToStep: Record = { + [CLAIM_CASE_STATUS.CREATED]: CLAIM_STEP.CLAIM_CREATED, + [CLAIM_CASE_STATUS.SELECTING_OUTER_PARTS]: CLAIM_STEP.SELECT_OUTER_PARTS, + [CLAIM_CASE_STATUS.SELECTING_OTHER_PARTS]: CLAIM_STEP.SELECT_OTHER_PARTS, + [CLAIM_CASE_STATUS.CAPTURING_PART_DAMAGES]: CLAIM_STEP.CAPTURE_PART_DAMAGES, + [CLAIM_CASE_STATUS.UPLOADING_REQUIRED_DOCUMENTS]: CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + [CLAIM_CASE_STATUS.WAITING_FOR_FILE_REVIEWER]: CLAIM_STEP.CLAIM_CREATED, + }; + const step = statusToStep[String(scenario.claimStatus)] ?? CLAIM_STEP.SELECT_OUTER_PARTS; + return { + ...base, + currentStep: step, + nextStep: + step === CLAIM_STEP.CLAIM_CREATED + ? CLAIM_STEP.SELECT_OUTER_PARTS + : step === CLAIM_STEP.SELECT_OUTER_PARTS + ? CLAIM_STEP.SELECT_OTHER_PARTS + : step === CLAIM_STEP.SELECT_OTHER_PARTS + ? CLAIM_STEP.CAPTURE_PART_DAMAGES + : step === CLAIM_STEP.CAPTURE_PART_DAMAGES + ? CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS + : CLAIM_STEP.USER_SUBMISSION_COMPLETE, + }; + } + case "queue": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + ], + currentStep: CLAIM_STEP.USER_SUBMISSION_COMPLETE, + nextStep: CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + assignedForReviewBy: scenario.claimActorKind === "field_expert" ? assignedField : undefined, + }; + case "reviewing": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + ], + currentStep: CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + nextStep: CLAIM_STEP.INSURER_REVIEW, + locked: true, + lockedAt: addMinutes(createdAt, 345), + expiredAt: addHours(addMinutes(createdAt, 345), 1), + assignedForReviewBy: assignedDamage, + lockedBy: assignedDamage, + }; + case "resend": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + ], + currentStep: CLAIM_STEP.USER_EXPERT_RESEND, + nextStep: CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + assignedForReviewBy: assignedDamage, + }; + case "factor_validation": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + CLAIM_STEP.OWNER_UPLOAD_FACTOR_DOCUMENTS, + ], + currentStep: CLAIM_STEP.EXPERT_COST_EVALUATION, + nextStep: CLAIM_STEP.INSURER_REVIEW, + locked: true, + lockedAt: addMinutes(createdAt, 510), + expiredAt: addHours(addMinutes(createdAt, 510), 1), + assignedForReviewBy: assignedDamage, + lockedBy: assignedDamage, + }; + case "insurer_review": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + CLAIM_STEP.INSURER_REVIEW, + ], + currentStep: CLAIM_STEP.INSURER_REVIEW, + nextStep: CLAIM_STEP.CLAIM_COMPLETED, + assignedForReviewBy: assignedDamage, + }; + case "file_maker_wait": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + CLAIM_STEP.INSURER_REVIEW, + CLAIM_STEP.CLAIM_COMPLETED, + ], + currentStep: CLAIM_STEP.CLAIM_COMPLETED, + nextStep: CLAIM_STEP.CLAIM_COMPLETED, + assignedForReviewBy: assignedDamage, + }; + case "file_maker_rejected": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + CLAIM_STEP.INSURER_REVIEW, + ], + currentStep: CLAIM_STEP.INSURER_REVIEW, + nextStep: CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + assignedForReviewBy: assignedDamage, + }; + case "completed": + case "rejected": + case "cancelled": + return { + ...base, + completedSteps: [ + CLAIM_STEP.CLAIM_CREATED, + CLAIM_STEP.SELECT_OUTER_PARTS, + CLAIM_STEP.SELECT_OTHER_PARTS, + CLAIM_STEP.CAPTURE_PART_DAMAGES, + CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS, + CLAIM_STEP.USER_SUBMISSION_COMPLETE, + CLAIM_STEP.EXPERT_DAMAGE_ASSESSMENT, + CLAIM_STEP.INSURER_REVIEW, + CLAIM_STEP.CLAIM_COMPLETED, + ], + currentStep: CLAIM_STEP.CLAIM_COMPLETED, + nextStep: CLAIM_STEP.CLAIM_COMPLETED, + assignedForReviewBy: assignedDamage, + }; + case "none": + return undefined; + } +} + +function buildClaimEvaluation( + scenario: Scenario, + actors: ReturnType, + index: number, +) { + if (!scenario.hasClaim) return undefined; + const damageExpert = actors.damageExpert; + const rating = scenario.rateClaim ? makeInsurerRating(index + 200) : undefined; + const reply = { + description: `Synthetic damage-expert reply #${index + 1}`, + actorDetail: { + actorId: String(damageExpert._id), + actorName: damageExpert.fullName, + }, + expertProfileSnapshot: expertProfileSnapshot(damageExpert), + parts: [ + { + partId: 1, + partName: "Front bumper", + side: "CENTER", + factorNeeded: + scenario.claimStage === "factor_validation" || + String(scenario.claimStatus) === CLAIM_CASE_STATUS.INSURER_REVIEW_MIXED_FACTORS_PENDING || + String(scenario.claimStatus) === CLAIM_CASE_STATUS.OWNER_REPAIR_FACTOR_UPLOAD_PENDING, + partPrice: `${4_500_000 + (index % 20) * 100_000}`, + }, + { + partId: 2, + partName: "Hood", + side: "CENTER", + factorNeeded: false, + partPrice: `${6_800_000 + (index % 15) * 120_000}`, + }, + ], + }; + + const out: AnyDoc = {}; + + if ( + scenario.claimStage === "reviewing" || + scenario.claimStage === "resend" || + scenario.claimStage === "factor_validation" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "rejected" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ) { + out.damageExpertReply = reply; + } + + if ( + scenario.claimStage === "factor_validation" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "rejected" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ) { + out.damageExpertReplyFinal = reply; + } + + if (scenario.claimStage === "resend") { + out.damageExpertResend = { + resendDescription: "Synthetic resend request", + resendDocuments: [{ kind: "green_card", label: "Green card" }], + resendCarParts: [{ id: "front-bumper", label_fa: "سپر جلو" }], + expertProfileSnapshot: expertProfileSnapshot(damageExpert), + }; + } + + if (scenario.claimStage === "factor_validation") { + out.factorValidationExpertProfileSnapshot = expertProfileSnapshot(damageExpert); + } + + if (scenario.objection) { + out.objection = { + submittedAt: new Date(), + reason: "Synthetic objection", + objectionParts: [{ partId: 1, reason: "Need review" }], + }; + } + + if (scenario.rateClaim) out.rating = rating; + + if (scenario.claimStage === "insurer_review") { + if (String(scenario.claimStatus) === CLAIM_CASE_STATUS.INSURER_REVIEW_MIXED_FACTORS_PENDING) { + out.ownerPricedPartsApproval = { + agree: true, + branchId: actors.damageExpert.branchId, + }; + } else if (String(scenario.claimStatus) === CLAIM_CASE_STATUS.OWNER_REPAIR_FACTOR_UPLOAD_PENDING) { + out.ownerPricedPartsApproval = { + agree: true, + branchId: actors.damageExpert.branchId, + }; + } else { + out.ownerInsurerApproval = { + agree: true, + branchId: actors.damageExpert.branchId, + }; + } + } + + if (scenario.claimStage === "completed" || scenario.claimStage === "file_maker_wait") { + out.ownerInsurerApproval = { agree: true, branchId: actors.damageExpert.branchId }; + } + + if (scenario.claimStage === "rejected") { + out.ownerInsurerApproval = { agree: false, branchId: actors.damageExpert.branchId }; + } + + if ( + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ) { + out.priceDrop = { + total: 0, + carPrice: 0, + carModel: 0, + }; + } + + return out; +} + +function buildClaimHistory( + scenario: Scenario, + actors: ReturnType, + createdAt: Date, + index: number, + blameRequestId: ObjectId, + publicId: string, +) { + if (!scenario.hasClaim) return undefined; + const history: AnyDoc[] = []; + + const creatorActor = + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? actorRef(actors.fieldExpert, "field_expert") + : scenario.flow === "V4" || scenario.flow === "V5" + ? actorRef(actors.fileReviewer, "file_reviewer") + : { actorType: "user", actorName: "User", actorId: new Types.ObjectId() }; + + pushHistory(history, createdAt, 210, "CLAIM_CREATED", creatorActor, { + blameRequestId, + blamePublicId: publicId, + }); + + const stepActor = + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? actorRef(actors.fieldExpert, "field_expert") + : scenario.flow === "V4" || scenario.flow === "V5" + ? actorRef(actors.fileReviewer, "file_reviewer") + : { actorType: "user", actorName: "User", actorId: new Types.ObjectId() }; + + const maybePushStep = (offset: number, stepKey: string) => + pushHistory(history, createdAt, offset, "STEP_COMPLETED", stepActor, { stepKey }); + + const pushFullUserFlow = + scenario.claimStage === "queue" || + scenario.claimStage === "reviewing" || + scenario.claimStage === "resend" || + scenario.claimStage === "factor_validation" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "rejected" || + scenario.claimStage === "cancelled" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected"; + + if (pushFullUserFlow) { + maybePushStep(235, CLAIM_STEP.SELECT_OUTER_PARTS); + maybePushStep(255, CLAIM_STEP.SELECT_OTHER_PARTS); + maybePushStep(275, CLAIM_STEP.CAPTURE_PART_DAMAGES); + maybePushStep(295, CLAIM_STEP.UPLOAD_REQUIRED_DOCUMENTS); + } else if (scenario.claimStage === "user_progress") { + const status = String(scenario.claimStatus); + if (status === CLAIM_CASE_STATUS.SELECTING_OTHER_PARTS) { + maybePushStep(235, CLAIM_STEP.SELECT_OUTER_PARTS); + } else if (status === CLAIM_CASE_STATUS.CAPTURING_PART_DAMAGES) { + maybePushStep(235, CLAIM_STEP.SELECT_OUTER_PARTS); + maybePushStep(255, CLAIM_STEP.SELECT_OTHER_PARTS); + } else if (status === CLAIM_CASE_STATUS.UPLOADING_REQUIRED_DOCUMENTS) { + maybePushStep(235, CLAIM_STEP.SELECT_OUTER_PARTS); + maybePushStep(255, CLAIM_STEP.SELECT_OTHER_PARTS); + maybePushStep(275, CLAIM_STEP.CAPTURE_PART_DAMAGES); + } + } + + if ( + scenario.claimStage === "queue" || + scenario.claimStage === "reviewing" || + scenario.claimStage === "resend" || + scenario.claimStage === "factor_validation" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "rejected" || + scenario.claimStage === "cancelled" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ) { + maybePushStep(315, CLAIM_STEP.USER_SUBMISSION_COMPLETE); + } + + if ( + scenario.claimStage === "reviewing" || + scenario.claimStage === "resend" || + scenario.claimStage === "factor_validation" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "rejected" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ) { + pushHistory( + history, + createdAt, + 345, + "CLAIM_ASSIGNED", + actorRef(actors.damageExpert, "damage_expert"), + { note: "Synthetic claim assignment" }, + ); + } + + if (scenario.claimStage === "resend") { + pushHistory( + history, + createdAt, + 390, + "EXPERT_RESEND_REQUESTED", + actorRef(actors.damageExpert, "damage_expert"), + { documentCount: 1, carPartCount: 1 }, + ); + } + + if ( + scenario.claimStage === "factor_validation" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "completed" || + scenario.claimStage === "rejected" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ) { + pushHistory( + history, + createdAt, + 420, + index % 2 === 0 ? "EXPERT_REPLY_SUBMITTED" : "EXPERT_FINAL_REPLY_SUBMITTED", + actorRef(actors.damageExpert, "damage_expert"), + { note: "Synthetic expert reply" }, + ); + } + + if (scenario.claimStage === "factor_validation") { + pushHistory( + history, + createdAt, + 450, + "OWNER_SIGNED_PRICED_PARTS_PENDING_FACTOR_UPLOAD", + { actorType: "user", actorName: "User" }, + ); + pushHistory( + history, + createdAt, + 510, + "ALL_FACTORS_UPLOADED_PENDING_VALIDATION", + { actorType: "user", actorName: "User" }, + ); + } + + if (scenario.claimStage === "insurer_review") { + const eventType = + String(scenario.claimStatus) === CLAIM_CASE_STATUS.INSURER_REVIEW_MIXED_FACTORS_PENDING || + String(scenario.claimStatus) === CLAIM_CASE_STATUS.OWNER_REPAIR_FACTOR_UPLOAD_PENDING + ? "OWNER_SIGNED_PRICED_PARTS_PENDING_FACTOR_UPLOAD" + : "OWNER_SIGNED_INSURER_APPROVAL"; + pushHistory(history, createdAt, 470, eventType, { actorType: "user", actorName: "User" }); + } + + if (scenario.claimStage === "file_maker_wait") { + pushHistory(history, createdAt, 500, "OWNER_SIGNED_INSURER_APPROVAL", { actorType: "user", actorName: "User" }); + pushHistory(history, createdAt, 530, "V5_HELD_FOR_FILE_MAKER_APPROVAL", { actorType: "system" }); + } + + if (scenario.claimStage === "file_maker_rejected") { + pushHistory(history, createdAt, 500, "OWNER_SIGNED_INSURER_APPROVAL", { actorType: "user", actorName: "User" }); + pushHistory(history, createdAt, 530, "V5_HELD_FOR_FILE_MAKER_APPROVAL", { actorType: "system" }); + pushHistory(history, createdAt, 560, "V5_FILE_MAKER_REJECTED", actorRef(actors.fileMaker, "file_maker"), { + reason: "Synthetic V5 correction request", + }); + } + + if (scenario.claimStage === "completed") { + pushHistory(history, createdAt, 500, "OWNER_SIGNED_INSURER_APPROVAL", { actorType: "user", actorName: "User" }); + pushHistory(history, createdAt, 550, "FANAVARAN_AUTO_SUBMIT_SUCCEEDED", { actorType: "system" }, { + trackingCode: `FNV-${pad(index + 1, 6)}`, + }); + pushHistory(history, createdAt, 610, "USER_RATING_SUBMITTED", { actorType: "user", actorName: "User" }); + } + + if (scenario.claimStage === "rejected") { + pushHistory(history, createdAt, 500, "OWNER_REJECTED_INSURER_APPROVAL_PRICING", { actorType: "user", actorName: "User" }); + pushHistory(history, createdAt, 520, "USER_OBJECTION_SUBMITTED", { actorType: "user", actorName: "User" }); + } + + if (scenario.claimStage === "cancelled") { + pushHistory(history, createdAt, 410, "EXPERT_RESEND_REQUESTED", actorRef(actors.damageExpert, "damage_expert"), { + note: "Cancelled after inactivity", + }); + } + + return history; +} + +function buildActivityEvents(args: { + seedTag: string; + scenario: Scenario; + index: number; + createdAt: Date; + clientId: ObjectId; + blameId: ObjectId; + claimId?: ObjectId; + actors: ReturnType; +}): AnyDoc[] { + const { scenario, index, createdAt, clientId, blameId, claimId, actors, seedTag } = args; + const events: AnyDoc[] = []; + + const pushActivity = ( + expertId: ObjectId, + fileId: ObjectId, + fileType: string, + eventType: string, + occurredAt: Date, + idempotencyKey: string, + ) => { + events.push({ + expertId, + tenantId: clientId, + fileId, + fileType, + eventType, + occurredAt, + idempotencyKey, + seedMeta: { script: SCRIPT_NAME, tag: seedTag }, + }); + }; + + const actorForBlame = + scenario.blameKind === "expert" + ? actors.expert + : scenario.blameKind === "field_expert" + ? actors.fieldExpert + : undefined; + + if (actorForBlame && scenario.blameActivity) { + const t1 = addMinutes(createdAt, 125); + const base = `${SCRIPT_NAME}:${seedTag}:blame:${blameId.toHexString()}:${actorForBlame._id.toHexString()}`; + pushActivity(actorForBlame._id, blameId, FILE_KIND.BLAME, ACTIVITY.CHECKED, t1, `${base}:checked`); + if (scenario.blameActivity === "handled") { + pushActivity( + actorForBlame._id, + blameId, + FILE_KIND.BLAME, + ACTIVITY.HANDLED, + addMinutes(t1, 40), + `${base}:handled`, + ); + } + if (scenario.blameActivity === "unchecked") { + pushActivity( + actorForBlame._id, + blameId, + FILE_KIND.BLAME, + ACTIVITY.UNCHECKED, + addMinutes(t1, 35), + `${base}:unchecked`, + ); + } + } + + const actorForClaim = + scenario.claimActorKind === "damage_expert" + ? actors.damageExpert + : scenario.claimActorKind === "field_expert" + ? actors.fieldExpert + : undefined; + + if (claimId && actorForClaim && scenario.claimActivity) { + const t1 = addMinutes(createdAt, 350); + const base = `${SCRIPT_NAME}:${seedTag}:claim:${claimId.toHexString()}:${actorForClaim._id.toHexString()}`; + pushActivity(actorForClaim._id, claimId, FILE_KIND.CLAIM, ACTIVITY.CHECKED, t1, `${base}:checked`); + if (scenario.claimActivity === "handled") { + pushActivity( + actorForClaim._id, + claimId, + FILE_KIND.CLAIM, + ACTIVITY.HANDLED, + addMinutes(t1, 55), + `${base}:handled`, + ); + } + if (scenario.claimActivity === "unchecked") { + pushActivity( + actorForClaim._id, + claimId, + FILE_KIND.CLAIM, + ACTIVITY.UNCHECKED, + addMinutes(t1, 30), + `${base}:unchecked`, + ); + } + } + + + return events; +} + +function buildCaseDocuments(ctx: SeedContext, scenario: Scenario, index: number) { + const actors = pickActorsForCase(ctx, index); + const createdAt = buildCreatedAt(index, ctx.rng); + const publicId = `${ctx.publicPrefix}-${pad(index + 1)}`; + const blameId = new Types.ObjectId(); + const claimId = scenario.hasClaim ? new Types.ObjectId() : undefined; + const partiesBundle = buildParties(ctx, scenario, index); + + const blameHistory = buildBlameHistory(ctx, scenario, actors, createdAt, scenario.fileType); + const blameUpdatedAt = blameHistory.length + ? new Date(blameHistory[blameHistory.length - 1].timestamp) + : createdAt; + + const blameAssignee = + scenario.blameKind === "expert" + ? actors.expert + : scenario.blameKind === "field_expert" + ? actors.fieldExpert + : undefined; + + const blameDoc: AnyDoc = { + _id: blameId, + requestNo: `BL-${ctx.publicPrefix}-${pad(index + 1, 6)}`, + publicId, + type: scenario.fileType, + status: scenario.blameStatus, + blameStatus: + scenario.fileType === "CAR_BODY" + ? "UNKNOWN" + : scenario.blameStatus === CASE_STATUS.WAITING_FOR_EXPERT + ? "DISAGREEMENT" + : "AGREED", + parties: partiesBundle.parties, + inquiries: {}, + history: blameHistory, + workflow: { + locked: scenario.blameActivity === "checked", + ...(blameAssignee + ? { + assignedForReviewBy: { + actorId: blameAssignee._id, + actorName: blameAssignee.fullName, + }, + } + : {}), + ...(scenario.blameActivity === "checked" && blameAssignee + ? { + lockedBy: { + actorId: blameAssignee._id, + actorName: blameAssignee.fullName, + }, + lockedAt: addMinutes(createdAt, 125), + expiredAt: addHours(addMinutes(createdAt, 125), 1), + } + : {}), + }, + expert: { + ...(blameAssignee ? { assignedExpertId: blameAssignee._id } : {}), + creationMethod: + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? CREATION_METHOD.IN_PERSON + : CREATION_METHOD.LINK, + filledBy: + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? FILLED_BY.EXPERT + : FILLED_BY.CUSTOMER, + decision: + scenario.blameStatus === CASE_STATUS.WAITING_FOR_EXPERT || + scenario.blameStatus === CASE_STATUS.WAITING_FOR_SECOND_PARTY + ? undefined + : (() => { + const decisionActorId = + scenario.blameKind === "expert" + ? actors.expert._id + : scenario.blameKind === "field_expert" + ? actors.fieldExpert._id + : undefined; + return { + guiltyPartyId: partiesBundle.firstPartyUserId, + description: "Synthetic seed decision", + decidedAt: addMinutes(createdAt, 180), + ...(decisionActorId ? { decidedByExpertId: decisionActorId } : {}), + }; + })(), + ...(scenario.rateBlame ? { rating: makeInsurerRating(index + 100) } : {}), + }, + snapshot: { parties: clone(partiesBundle.parties) }, + expertInitiated: scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3", + initiatedByFieldExpertId: + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? actors.fieldExpert._id + : undefined, + creationMethod: + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? CREATION_METHOD.IN_PERSON + : CREATION_METHOD.LINK, + filledBy: + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? FILLED_BY.EXPERT + : FILLED_BY.CUSTOMER, + isMadeByFileMaker: scenario.flow === "V4" || scenario.flow === "V5", + requiresFileMakerApproval: scenario.flow === "V5", + callCenterInitiated: scenario.flow === "V6", + initiatedByCallCenterId: scenario.flow === "V6" ? actors.callCenter._id : undefined, + skipInitialFormStep: scenario.flow === "V6", + createdAt, + updatedAt: blameUpdatedAt, + seedMeta: { + script: SCRIPT_NAME, + tag: ctx.seedTag, + flow: scenario.flow, + bucket: scenario.bucket, + }, + }; + + let claimDoc: AnyDoc | undefined; + if (scenario.hasClaim && claimId) { + const claimHistory = buildClaimHistory( + scenario, + actors, + createdAt, + index, + blameId, + publicId, + )!; + const claimUpdatedAt = claimHistory.length + ? new Date(claimHistory[claimHistory.length - 1].timestamp) + : addMinutes(createdAt, 210); + + const claimEvaluation = buildClaimEvaluation(scenario, actors, index); + claimDoc = { + _id: claimId, + requestNo: `CL-${ctx.publicPrefix}-${pad(index + 1, 6)}`, + publicId, + status: scenario.claimStatus, + claimStatus: scenario.claimReviewStatus, + blameDocumentResendPending: + scenario.blameStatus === CASE_STATUS.WAITING_FOR_DOCUMENT_RESEND, + blameRequestId: blameId, + blameRequestNo: blameDoc.requestNo, + initiatedByFieldExpertId: + scenario.flow === "V2_EXPERT_INIT" || scenario.flow === "V3" + ? actors.fieldExpert._id + : undefined, + damagedPartyUserId: partiesBundle.ownerUserId, + workflow: buildClaimWorkflow(scenario, actors, createdAt), + owner: { + userId: partiesBundle.ownerUserId, + fullName: partiesBundle.ownerFullName, + clientId: ctx.clientId, + userClientKey: ctx.clientId, + mobile: `091233${pad(index, 4)}`, + }, + vehicle: { + plate: makePlate(index + 900), + carName: scenario.fileType === "CAR_BODY" ? "Tara" : "Dena", + carModel: scenario.fileType === "CAR_BODY" ? "1402" : "1401", + carType: "sedan", + }, + money: { estimated: 11_000_000 + index * 10_000 }, + claimNo: 500_000 + index, + claimId: 700_000 + index, + dmgCaseId: 800_000 + index, + expertiseId: + scenario.claimStage === "completed" || + scenario.claimStage === "insurer_review" || + scenario.claimStage === "rejected" || + scenario.claimStage === "file_maker_wait" || + scenario.claimStage === "file_maker_rejected" + ? 900_000 + index + : undefined, + fanavaranSync: + scenario.claimStage === "completed" + ? { + baseClaim: { status: "success", submittedAt: addMinutes(createdAt, 240) }, + damageCase: { status: "success", submittedAt: addMinutes(createdAt, 260) }, + attachments: { status: "success", submittedAt: addMinutes(createdAt, 280) }, + expertise: { status: "success", submittedAt: addMinutes(createdAt, 300) }, + } + : undefined, + damage: { + selectedParts: makeDamageParts(index), + otherParts: index % 4 === 0 ? ["windshield"] : [], + expertAddedParts: index % 7 === 0 ? [{ partName: "Mirror", side: "LEFT" }] : [], + }, + media: {}, + evaluation: claimEvaluation, + inquiries: {}, + snapshot: { + accident: { type: scenario.fileType }, + parties: clone(partiesBundle.parties), + }, + requiredDocuments: makeRequiredDocuments(index, scenario.claimStage), + history: claimHistory, + userRating: scenario.userRated ? makeUserRating(index) : undefined, + requiresFileMakerApproval: scenario.flow === "V5", + fileMakerApprovalActorId: scenario.flow === "V5" ? actors.fileMaker._id : undefined, + fileMakerRejectionCount: + scenario.claimStage === "file_maker_rejected" ? 1 : 0, + fileMakerRejectionReason: + scenario.claimStage === "file_maker_rejected" + ? "Synthetic file-maker rejection" + : undefined, + createdAt: addMinutes(createdAt, 210), + updatedAt: claimUpdatedAt, + seedMeta: { + script: SCRIPT_NAME, + tag: ctx.seedTag, + flow: scenario.flow, + bucket: scenario.bucket, + }, + }; + } + + const activityEvents = buildActivityEvents({ + seedTag: ctx.seedTag, + scenario, + index, + createdAt, + clientId: ctx.clientId, + blameId, + claimId, + actors, + }); + + return { blameDoc, claimDoc, activityEvents, publicId }; +} + +async function main() { + loadEnvFile(); + const mongoUri = resolveMongoUri(); + const count = Math.max(1, Number(process.env.SEED_REPORTS_COUNT?.trim() || "1000")); + const seedTag = process.env.SEED_REPORTS_TAG?.trim() || "reports-load-v1"; + const publicPrefix = + process.env.SEED_REPORTS_PUBLIC_PREFIX?.trim() || `RPT${shortHash(seedTag, 3)}`; + const defaultPassword = + process.env.SEED_REPORTS_DEFAULT_PASSWORD?.trim() || "Reports@724"; + + await mongoose.connect(mongoUri, { + tls: process.env.MONGO_TLS === "true", + tlsAllowInvalidCertificates: + process.env.MONGO_TLS_ALLOW_INVALID_CERTS === "true", + }); + const db = mongoose.connection.db; + if (!db) throw new Error("Mongo connection has no db handle"); + + const client = await resolveClient(db); + const clientId = asObjectId(client._id); + const clientLabel = + client?.clientName?.persian || client?.clientName?.english || String(client._id); + + const rng = mulberry32(seedToInt(`${seedTag}:${clientId.toHexString()}:${count}`)); + const passwordHash = await hashPassword(defaultPassword); + const branches = await ensureBranches(db, clientId, String(clientLabel)); + const roster = await ensureRoster({ + db, + clientId, + branches, + passwordHash, + publicPrefix, + }); + + const purge = await purgeSeedPortfolio(db, seedTag); + + const ctx: SeedContext = { + client, + clientId, + clientIdStr: clientId.toHexString(), + count, + seedTag, + publicPrefix, + defaultPassword, + rng, + branches, + experts: roster.experts, + damageExperts: roster.damageExperts, + fieldExperts: roster.fieldExperts, + fileMakers: roster.fileMakers, + fileReviewers: roster.fileReviewers, + callCenters: roster.callCenters, + externalClientIds: Array.from({ length: 6 }, () => new Types.ObjectId()), + externalNames: [ + "بیمه تست الف", + "بیمه تست ب", + "بیمه تست ج", + "بیمه نمونه د", + "بیمه نمونه ه", + "بیمه نمونه و", + ], + }; + + const bucketQueue = buildBucketQueue(count, rng); + const bucketSeen: Record = {}; + const blameDocs: AnyDoc[] = []; + const claimDocs: AnyDoc[] = []; + const activityDocs: AnyDoc[] = []; + const summaryByBucket: Record = {}; + const summaryByFlow: Record = {}; + + for (let index = 0; index < bucketQueue.length; index++) { + const bucket = bucketQueue[index]; + bucketSeen[bucket] = (bucketSeen[bucket] ?? 0) + 1; + const flow = selectFlow(bucket, bucketSeen[bucket] - 1); + const fileType = selectFileType(bucket, flow, index); + const scenario = makeScenario(bucket, flow, fileType, index); + const { blameDoc, claimDoc, activityEvents } = buildCaseDocuments(ctx, scenario, index); + blameDocs.push(blameDoc); + if (claimDoc) claimDocs.push(claimDoc); + activityDocs.push(...activityEvents); + summaryByBucket[bucket] = (summaryByBucket[bucket] ?? 0) + 1; + summaryByFlow[flow] = (summaryByFlow[flow] ?? 0) + 1; + } + + if (blameDocs.length) await db.collection("blameCases").insertMany(blameDocs, { ordered: false }); + if (claimDocs.length) await db.collection("claimCases").insertMany(claimDocs, { ordered: false }); + if (activityDocs.length) { + await db.collection("expertFileActivities").insertMany(activityDocs, { ordered: false }); + } + + console.log("\n=== Insurer reports seed complete ==="); + console.log(`Client : ${clientLabel} (${clientId.toHexString()})`); + console.log(`Seed tag : ${seedTag}`); + console.log(`Public prefix : ${publicPrefix}`); + console.log(`Password : ${defaultPassword}`); + console.log(`Branches : ${branches.length}`); + console.log(`Experts : ${roster.experts.length} blame / ${roster.damageExperts.length} damage / ${roster.fieldExperts.length} field`); + console.log(`Extra actors : ${roster.fileMakers.length} file-makers / ${roster.fileReviewers.length} file-reviewers / ${roster.callCenters.length} call-center`); + console.log(`Purged : ${purge.deletedBlames} blame, ${purge.deletedClaims} claim, ${purge.deletedActivities} activities`); + console.log(`Inserted : ${blameDocs.length} blame, ${claimDocs.length} claim, ${activityDocs.length} activities`); + console.log("\nBy unified bucket:"); + for (const key of Object.keys(summaryByBucket).sort()) { + console.log(`- ${key}: ${summaryByBucket[key]}`); + } + console.log("\nBy flow:"); + for (const key of Object.keys(summaryByFlow).sort()) { + console.log(`- ${key}: ${summaryByFlow[key]}`); + } +} + +main() + .catch((error) => { + console.error("Seed failed:", error); + process.exitCode = 1; + }) + .finally(async () => { + try { + await mongoose.disconnect(); + } catch { + // ignore disconnect errors on exit + } + });